You've already forked fm-dx-webserver
mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-02-26 14:11:59 +01:00
maps.fmdx.pl integration, various bugfixes, UI changes
This commit is contained in:
@@ -139,6 +139,7 @@ var dataToSend = {
|
||||
af: [],
|
||||
rt0: '',
|
||||
rt1: '',
|
||||
users: ''
|
||||
};
|
||||
|
||||
const initialData = {
|
||||
@@ -153,6 +154,7 @@ const initialData = {
|
||||
af: [],
|
||||
rt0: '',
|
||||
rt1: '',
|
||||
users: ''
|
||||
};
|
||||
|
||||
const resetToDefault = dataToSend => Object.assign(dataToSend, initialData);
|
||||
@@ -244,6 +246,12 @@ function handleData(ws, receivedData) {
|
||||
}
|
||||
}
|
||||
|
||||
function showOnlineUsers(currentUsers) {
|
||||
console.log(currentUsers);
|
||||
dataToSend.users = currentUsers;
|
||||
initialData.users = currentUsers;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleData
|
||||
handleData, showOnlineUsers
|
||||
};
|
||||
13
index.js
13
index.js
@@ -20,11 +20,7 @@ const dataHandler = require('./datahandler');
|
||||
const config = require('./userconfig');
|
||||
|
||||
/* Server settings */
|
||||
const webServerHost = config.webServerHost; // IP of the web server
|
||||
const webServerPort = config.webServerPort; // web server port
|
||||
const xdrdServerHost = config.xdrdServerHost; // xdrd server iP
|
||||
const xdrdServerPort = config.xdrdServerPort; // xdrd server port
|
||||
const xdrdPassword = config.xdrdPassword;
|
||||
const { webServerHost, webServerPort, xdrdServerHost, xdrdServerPort, xdrdPassword, qthLatitude, qthLongitude } = config;
|
||||
|
||||
const wss = new WebSocket.Server({ noServer: true });
|
||||
|
||||
@@ -37,6 +33,7 @@ const client = new net.Socket();
|
||||
wss.on('connection', (ws, request) => {
|
||||
const clientIp = request.connection.remoteAddress;
|
||||
currentUsers++;
|
||||
dataHandler.showOnlineUsers(currentUsers);
|
||||
console.log(infoMsg, `WebSocket client connected\nIP: ${clientIp}\nUsers online: ${currentUsers}`);
|
||||
|
||||
ws.on('message', (message) => {
|
||||
@@ -49,6 +46,7 @@ wss.on('connection', (ws, request) => {
|
||||
|
||||
ws.on('close', (code, reason) => {
|
||||
currentUsers--;
|
||||
dataHandler.showOnlineUsers(currentUsers);
|
||||
console.log(infoMsg, `WebSocket client disconnected\nIP: ${clientIp}\nCode: ${code} ${reason}\nUsers online: ${currentUsers}`);
|
||||
});
|
||||
|
||||
@@ -121,6 +119,11 @@ httpServer.listen(webServerPort, webServerHost, () => {
|
||||
console.log(infoMsg, `Web server is running at \x1b[34mhttp://${webServerHost}:${webServerPort}\x1b[0m.`);
|
||||
});
|
||||
|
||||
|
||||
app.get('/coordinates', (req, res) => {
|
||||
// Sending the coordinates to the client
|
||||
res.json({ qthLatitude, qthLongitude });
|
||||
});
|
||||
/* Audio */
|
||||
|
||||
app.get('/audio-proxy', (req, res) => {
|
||||
|
||||
@@ -3,10 +3,13 @@ const webServerPort = 8080; // web server port
|
||||
|
||||
const xdrdServerHost = '192.168.1.15'; // xdrd server iP
|
||||
const xdrdServerPort = 7373; // xdrd server port
|
||||
const xdrdPassword = ''; // xdrd password (optional)
|
||||
const xdrdPassword = 'changeme'; // xdrd password (optional)
|
||||
|
||||
const qthLatitude = '0.0'; // your latitude, useful for maps.fmdx.pl integration
|
||||
const qthLongitude = '0.0'; // your longitude, useful for maps.fmdx.pl integration
|
||||
|
||||
const verboseMode = false; // if true, console will display extra messages
|
||||
|
||||
module.exports = {
|
||||
webServerHost, webServerPort, xdrdServerHost, xdrdServerPort, xdrdPassword, verboseMode
|
||||
};
|
||||
webServerHost, webServerPort, xdrdServerHost, xdrdServerPort, xdrdPassword, qthLatitude, qthLongitude, verboseMode
|
||||
};
|
||||
@@ -12,6 +12,22 @@
|
||||
--color-5: color-mix(in srgb, var(--color-main) 0%, var(--color-main-bright));
|
||||
}
|
||||
|
||||
.color-1 {
|
||||
color: var(--color-1);
|
||||
}
|
||||
|
||||
.color-2 {
|
||||
color: var(--color-2);
|
||||
}
|
||||
|
||||
.color-3 {
|
||||
color: var(--color-3);
|
||||
}
|
||||
|
||||
.color-4 {
|
||||
color: var(--color-4);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--color-main-bright);
|
||||
color: inherit;
|
||||
@@ -115,6 +131,7 @@ h2 {
|
||||
border-radius: 30px;
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
transition: 0.3s ease-in-out background-color;
|
||||
}
|
||||
|
||||
.panel-90 {
|
||||
@@ -158,12 +175,16 @@ h2 {
|
||||
/*font-family: "Monomaniac One", sans-serif;*/
|
||||
}
|
||||
|
||||
.text-medium-big {
|
||||
.text-medium {
|
||||
font-size: 24px;
|
||||
color: #aaa;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.text-medium-big {
|
||||
font-size: 46px;
|
||||
}
|
||||
|
||||
.text-small {
|
||||
font-size: 13px;
|
||||
}
|
||||
@@ -519,11 +540,7 @@ input[type="range"]::-moz-range-thumb {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.themes-list select {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#theme-selector {
|
||||
select {
|
||||
height: 42px;
|
||||
width: 150px;
|
||||
padding: 10px;
|
||||
@@ -533,8 +550,17 @@ input[type="range"]::-moz-range-thumb {
|
||||
border-bottom: 4px solid var(--color-2);
|
||||
cursor: pointer;
|
||||
transition: 0.35s ease-in-out background;
|
||||
font-family: inherit;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#theme-selector:hover {
|
||||
select option {
|
||||
font-family: 'Titillium Web', sans-serif;
|
||||
font-weight: 300;
|
||||
padding: 10px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
select:hover {
|
||||
background: var(--color-5);
|
||||
}
|
||||
BIN
web/favicon.png
Normal file
BIN
web/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
web/favicon2.png
Normal file
BIN
web/favicon2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
@@ -2,8 +2,10 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>FM-DX Webserver [Noobish's Server]</title>
|
||||
<link href="/styles.css" type="text/css" rel="stylesheet">
|
||||
<link href="css/styles.css" type="text/css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" type="text/css" rel="stylesheet">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<link rel="icon" type="image/png" href="favicon2.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
@@ -13,16 +15,18 @@
|
||||
<div class="flex-container">
|
||||
<div class="panel-90 no-bg">
|
||||
<div class="flex-container">
|
||||
<div class="panel-75" style="height: 110px;">
|
||||
<div class="panel-75 hover-brighten" id="ps-container" style="height: 110px;">
|
||||
<span class="text-big" id="data-ps"></span>
|
||||
</div>
|
||||
|
||||
<div class="panel-33">
|
||||
<h2 style="padding-top: 18px;">
|
||||
<span id="data-pty" style="color: #eee;"></span>
|
||||
<span style="margin-left: 30px;" id="data-tp">TP</span>
|
||||
<span style="margin-left: 15px; color: #ff5776;" id="data-st"></span>
|
||||
<h2>
|
||||
<div id="data-pty" style="color:white;"></div>
|
||||
</h2>
|
||||
<h3 style="margin-top:5px;">
|
||||
<span id="data-tp" class="color-4">TP</span>
|
||||
<span style="margin-left: 15px; color: #ff5776;" id="data-st"></span>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,8 +45,9 @@
|
||||
<div class="panel-33">
|
||||
<h2>SIGNAL</h2>
|
||||
<span class="text-big">
|
||||
<span id="data-signal"></span>
|
||||
<span id="signal-units" class="text-medium-big">dBf</span>
|
||||
<span id="data-signal"></span><!--
|
||||
--><span id="data-signal-decimal" class="text-medium-big" style="color: #ccc;"></span>
|
||||
<span id="signal-units" class="text-medium">dBf</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,7 +107,7 @@
|
||||
<span class="close" id="closeModal"><i class="fa-solid fa-xmark"></i></span>
|
||||
|
||||
<label for="themes" style="margin-top: 50px;"><i class="fa-solid fa-palette"></i> Theme:</label>
|
||||
<select name="themes" style="margin-bottom: 50px;" id="theme-selector">
|
||||
<select name="themes" style="margin-bottom: 15px;" id="theme-selector">
|
||||
<option value="theme1">Blurple</option>
|
||||
<option value="theme2">Red</option>
|
||||
<option value="theme3">Green</option>
|
||||
@@ -110,6 +115,11 @@
|
||||
<option value="theme5">Orange</option>
|
||||
<option value="theme6">Pink</option>
|
||||
</select>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label for="themes"><i class="fa-solid fa-user"></i> Users online:</label>
|
||||
<span id="users-online" name="users-online">0</span>
|
||||
</div>
|
||||
<p class="text-small" style="margin-bottom: 50px;">FM-DX WebServer uses librdsparser by <a href="https://fmdx.pl" target="_blank">Konrad Kosmatka</a>.</p>
|
||||
<button class="button-close" id="closeModalButton">Close</button>
|
||||
</div>
|
||||
|
||||
88
web/main.js
88
web/main.js
@@ -38,6 +38,19 @@ function zoomOut() {
|
||||
zoomMaxValue *= 1.1;
|
||||
}
|
||||
|
||||
function getInitialSettings() {
|
||||
fetch('/coordinates')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Use the received data (data.qthLatitude, data.qthLongitude) as needed
|
||||
localStorage.setItem('qthLatitude', data.qthLatitude);
|
||||
localStorage.setItem('qthLongitude', data.qthLongitude);
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
|
||||
getInitialSettings();
|
||||
|
||||
function updateCanvas() {
|
||||
// Remove old data when it exceeds the maximum data points
|
||||
|
||||
@@ -179,7 +192,14 @@ function updatePanels(parsedData) {
|
||||
document.querySelector('#data-st').innerHTML = parsedData.st === false ? "<span class='text-gray'>ST</span>" : "ST";
|
||||
document.querySelector('#data-rt0').innerHTML = parsedData.rt0;
|
||||
document.querySelector('#data-rt1').innerHTML = parsedData.rt1;
|
||||
document.querySelector('#data-signal').textContent = signalToggle.checked ? (parsedData.signal - 11.75).toFixed(1) : parsedData.signal;
|
||||
|
||||
const signalValue = signalToggle.checked ? (parsedData.signal - 11.75) : parsedData.signal;
|
||||
const integerPart = Math.floor(signalValue);
|
||||
const decimalPart = (signalValue - integerPart).toFixed(1).slice(1); // Adjusted this line
|
||||
|
||||
document.querySelector('#data-signal').textContent = integerPart;
|
||||
document.querySelector('#data-signal-decimal').textContent = decimalPart;
|
||||
document.querySelector('#users-online').textContent = parsedData.users;
|
||||
}
|
||||
|
||||
signalToggle.addEventListener("change", function() {
|
||||
@@ -194,26 +214,43 @@ signalToggle.addEventListener("change", function() {
|
||||
|
||||
const textInput = document.getElementById('commandinput');
|
||||
|
||||
textInput.addEventListener('change', function (event) {
|
||||
const inputValue = textInput.value;
|
||||
// Check if the user agent contains 'iPhone'
|
||||
if (/iPhone/i.test(navigator.userAgent) && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(inputValue);
|
||||
// Clear the input field if needed
|
||||
textInput.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
textInput.addEventListener('keyup', function (event) {
|
||||
// Get the current input value
|
||||
let inputValue = textInput.value;
|
||||
// Check if the pressed key is 'Backspace' (key code 8)
|
||||
if (event.key !== 'Backspace') {
|
||||
// Get the current input value
|
||||
let inputValue = textInput.value;
|
||||
|
||||
// Remove non-digit characters
|
||||
inputValue = inputValue.replace(/[^0-9]/g, '');
|
||||
// Remove non-digit characters (excluding dot)
|
||||
inputValue = inputValue.replace(/[^0-9.]/g, '');
|
||||
|
||||
console.log("InputValue contains dot: ", inputValue.toLowerCase().includes("."));
|
||||
|
||||
// Determine where to add the dot based on the frequency range
|
||||
if (inputValue.includes(".") === false) {
|
||||
if (inputValue.startsWith('10') && inputValue.length > 2) {
|
||||
// For frequencies starting with '10', add the dot after the third digit
|
||||
inputValue = inputValue.slice(0, 3) + '.' + inputValue.slice(3);
|
||||
textInput.value = inputValue;
|
||||
} else if (inputValue.length > 2) {
|
||||
// For other frequencies, add the dot after the second digit
|
||||
inputValue = inputValue.slice(0, 2) + '.' + inputValue.slice(2);
|
||||
// Remove the last dot if there are two consecutive dots
|
||||
if (inputValue.includes("..")) {
|
||||
inputValue = inputValue.slice(0, inputValue.lastIndexOf('.')) + inputValue.slice(inputValue.lastIndexOf('.') + 1);
|
||||
textInput.value = inputValue;
|
||||
}
|
||||
|
||||
// Determine where to add the dot based on the frequency range
|
||||
if (!inputValue.includes(".")) {
|
||||
if (inputValue.startsWith('10') && inputValue.length > 2) {
|
||||
// For frequencies starting with '10', add the dot after the third digit
|
||||
inputValue = inputValue.slice(0, 3) + '.' + inputValue.slice(3);
|
||||
textInput.value = inputValue;
|
||||
} else if (inputValue.length > 2) {
|
||||
// For other frequencies, add the dot after the second digit
|
||||
inputValue = inputValue.slice(0, 2) + '.' + inputValue.slice(2);
|
||||
textInput.value = inputValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the input value
|
||||
@@ -266,12 +303,14 @@ function getCurrentFreq() {
|
||||
|
||||
freqUpButton = document.getElementById('freq-up');
|
||||
freqDownButton = document.getElementById('freq-down');
|
||||
psContainer = document.getElementById('ps-container');
|
||||
piCodeContainer = document.getElementById('pi-code-container');
|
||||
freqContainer = document.getElementById('freq-container');
|
||||
|
||||
freqUpButton.addEventListener("click", tuneUp);
|
||||
freqDownButton.addEventListener("click", tuneDown);
|
||||
piCodeContainer.addEventListener("click", copyPi);
|
||||
psContainer.addEventListener("click", copyPs);
|
||||
piCodeContainer.addEventListener("click", findOnMaps);
|
||||
freqContainer.addEventListener("click", function() {
|
||||
textInput.focus();
|
||||
});
|
||||
@@ -290,20 +329,29 @@ function tuneDown() {
|
||||
}
|
||||
}
|
||||
|
||||
async function copyPi() {
|
||||
console.log("clicked pi");
|
||||
async function copyPs() {
|
||||
let frequency = document.querySelector('#data-frequency').textContent;
|
||||
let pi = document.querySelector('#data-pi').textContent;
|
||||
let ps = document.querySelector('#data-ps').textContent;
|
||||
let signal = document.querySelector('#data-signal').textContent;
|
||||
let signalDecimal = document.querySelector('#data-signal-decimal').textContent;
|
||||
let signalUnit = document.querySelector('#signal-units').textContent;
|
||||
try {
|
||||
await copyToClipboard(frequency + " - " + pi + " | " + ps + " [" + signal + " " + signalUnit + "]");
|
||||
await copyToClipboard(frequency + " - " + pi + " | " + ps + " [" + signal + signalDecimal + " " + signalUnit + "]");
|
||||
} catch(error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function findOnMaps() {
|
||||
let frequency = document.querySelector('#data-frequency').textContent;
|
||||
let pi = document.querySelector('#data-pi').textContent;
|
||||
let latitude = localStorage.getItem('qthLongitude');
|
||||
let longitude = localStorage.getItem('qthLatitude');
|
||||
frequency = parseFloat(frequency).toFixed(1);
|
||||
window.open("https://maps.fmdx.pl/#qth=" + longitude + "," + latitude + "&freq=" + frequency + "&pi=" + pi, "_blank");
|
||||
}
|
||||
|
||||
async function copyToClipboard(textToCopy) {
|
||||
// Navigator clipboard api needs a secure context (https)
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
|
||||
Reference in New Issue
Block a user