added docker compose and plugins for openwebrx installation
42
docker/openwebrx/docker-compose.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
services:
|
||||
openwebrx:
|
||||
image: 'slechev/openwebrxplus-softmbe:latest'
|
||||
container_name: openwebrx_docker
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '8073:8073'
|
||||
- '5678:5678'
|
||||
environment:
|
||||
TZ: Europe/Berlin
|
||||
FORWARD_LOCALPORT_1234: 5678
|
||||
# OPENWEBRX_ADMIN_USER: admin
|
||||
# OPENWEBRX_ADMIN_PASSWORD: 123465
|
||||
HEALTHCHECK_USB_1df7_3000: 1 # SDRPlay Healthcheck
|
||||
HEALTHCHECK_USB_0bda_2838: 1 # NeSDR Smart Healthcheck
|
||||
devices:
|
||||
- /dev/bus/usb:/dev/bus/usb
|
||||
volumes:
|
||||
- ./etc:/etc/openwebrx
|
||||
- ./var:/var/lib/openwebrx
|
||||
- ./plugins:/usr/lib/python3/dist-packages/htdocs/plugins
|
||||
|
||||
# if you want your container to restart automatically if the HEALTHCHECK fails
|
||||
# (see here: https://stackoverflow.com/a/48538213/420585)
|
||||
openwebrx_autoheal:
|
||||
restart: unless-stopped
|
||||
container_name: openwebrx_docker_autoheal
|
||||
image: willfarrell/autoheal
|
||||
environment:
|
||||
- AUTOHEAL_CONTAINER_LABEL=all
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
restarter:
|
||||
image: docker:cli
|
||||
container_name: openwebrx_docker_restarter
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./restarter:/restarter
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command: ["chmod +x /restarter/script.sh && /restarter/script.sh"]
|
||||
restart: unless-stopped
|
||||
57
docker/openwebrx/plugins/receiver/antenna_switcher/README.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Antenna Switcher"
|
||||
permalink: /receiver/antenna_switcher
|
||||
---
|
||||
|
||||
This is a `receiver` plugin to add antenna switching functionality for Raspberry Pi devices, providing logical levels on their GPIO ports that correspond to the user's antenna selection via buttons on the OWRX's front-end.
|
||||
|
||||
It consists of a **front-end** and **back-end** part.
|
||||
|
||||
The front-end is a standard OpenWebRX+ plugin and its installation does not differ from the standard way you [install plugins](/openwebrxplus-plugins/#load-plugins):
|
||||
|
||||
## I. Front-end installation
|
||||
|
||||
### Load
|
||||
|
||||
Add this line in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/antenna_switcher/antenna_switcher.js');
|
||||
```
|
||||
|
||||
#### Back-end URL
|
||||
|
||||
You probably don't want to change the default back-end instance URL, but if you do want to, you can do it from `init.js`, before or after the plugin loading, with:
|
||||
|
||||
`Plugins.antenna_switcher.API_URL = 'HOST:PORT/antenna_switch'`
|
||||
|
||||
### init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
|
||||
## II. Back-end installation
|
||||
|
||||
The back-end installation script **is designed to work with OpenWebRX+ installed from the repository as a package**. You need to adjust it in case you want to use it inside a Docker container.
|
||||
|
||||
For the back-end, download and run the [install_backend.sh](install_backend.sh) Bash script **as root**:
|
||||
|
||||
```sh
|
||||
wget 'https://0xaf.github.io/openwebrxplus-plugins/receiver/antenna_switcher/install_backend.sh'
|
||||
chmod +x ./install_backend.sh
|
||||
sudo ./install_backend.sh
|
||||
```
|
||||
|
||||
The script will download and install the Flask back-end in a Python virtual environment located in `/opt/antenna_switcher`, so you don't have to worry about libraries messing up your system's default Python installation.
|
||||
|
||||
It will also create a systemd service file called `antenna_switcher`, then start and enable it.
|
||||
|
||||
The nginx configuration will be extended to provide reverse proxying from the OpenWebRX front-end *(default port 8073)* to the Flask back-end *(port 8075, binding only on 127.0.0.1)*.
|
||||
|
||||
## Configuration
|
||||
|
||||
Things like which GPIO pins to raise high when the corresponding button from the front-end is selected can be configured in `/opt/antenna_switcher/antenna_switcher.cfg`.
|
||||
|
||||
```sh
|
||||
sudo ${EDITOR-nano} /opt/antenna_switcher/antenna_switcher.cfg
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
antenna_pins=[23,24]
|
||||
@@ -0,0 +1,24 @@
|
||||
.openwebrx-ant-grid {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: -5px -5px 0 0;
|
||||
}
|
||||
|
||||
.openwebrx-ant-grid {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
flex: 1 0 38px;
|
||||
margin: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
@supports(gap: 5px) {
|
||||
.openwebrx-ant-grid {
|
||||
margin: 0;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.openwebrx-ants-grid {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Antenna switch UI plugin for OpenWebRX+
|
||||
// License: MIT
|
||||
// Original Example File Copyright (c) 2023 Stanislav Lechev [0xAF], LZ2SLL
|
||||
// Modified by DL9UL to provide UI buttons used to call a WebAPI
|
||||
// Re-written by Dimitar Milkov, LZ2DMV to a more optimized and clean state
|
||||
|
||||
Plugins.antenna_switcher.API_URL ??= `${window.location.origin}/antenna_switch`;
|
||||
|
||||
// Init function of the plugin
|
||||
Plugins.antenna_switcher.init = function () {
|
||||
|
||||
let antennaNum = 0;
|
||||
const buttons = [];
|
||||
|
||||
// Function to send a command via POST
|
||||
function sendCommand(command) {
|
||||
fetch(Plugins.antenna_switcher.API_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ command }),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
const response = data.payload.response;
|
||||
const match = response.match(/^n:(\d)$/);
|
||||
if (match) {
|
||||
antennaNum = parseInt(match[1], 10);
|
||||
if (!buttonsCreated) createButtons();
|
||||
} else {
|
||||
updateButtonState(response);
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
|
||||
// Function to update the button state based on the active antenna
|
||||
function updateButtonState(activeAntenna) {
|
||||
buttons.forEach((button, index) => {
|
||||
button.classList.toggle('highlighted', (index + 1).toString() === activeAntenna);
|
||||
});
|
||||
}
|
||||
|
||||
// Create buttons and add them to the container
|
||||
function createButtons() {
|
||||
// Create antenna section
|
||||
const antSection = document.createElement('div');
|
||||
antSection.classList.add('openwebrx-section');
|
||||
|
||||
const antPanelLine = document.createElement('div');
|
||||
antPanelLine.classList.add('openwebrx-ant', 'openwebrx-panel-line');
|
||||
antSection.appendChild(antPanelLine);
|
||||
|
||||
const antGrid = document.createElement('div');
|
||||
antGrid.classList.add('openwebrx-ant-grid');
|
||||
antPanelLine.appendChild(antGrid);
|
||||
|
||||
for (let i = 1; i <= antennaNum; i++) {
|
||||
const button = createButton(i);
|
||||
buttons.push(button);
|
||||
antGrid.appendChild(button);
|
||||
}
|
||||
|
||||
// Section Divider to hide ANT panel
|
||||
const antSectionDivider = document.createElement('div');
|
||||
antSectionDivider.id = 'openwebrx-section-ant';
|
||||
antSectionDivider.classList.add('openwebrx-section-divider');
|
||||
antSectionDivider.onclick = () => UI.toggleSection(antSectionDivider);
|
||||
antSectionDivider.innerHTML = "▾ Antenna";
|
||||
|
||||
// Append the container above the "openwebrx-section-modes"
|
||||
const targetElement = document.getElementById('openwebrx-section-modes');
|
||||
targetElement.parentNode.insertBefore(antSectionDivider, targetElement);
|
||||
targetElement.parentNode.insertBefore(antSection, targetElement);
|
||||
|
||||
buttonsCreated = true;
|
||||
}
|
||||
|
||||
function createButton(i) {
|
||||
const button = document.createElement('div');
|
||||
button.id = `owrx-ant-button-${i}`;
|
||||
button.classList.add('openwebrx-button');
|
||||
button.textContent = `ANT ${i}`;
|
||||
button.onclick = () => sendCommand(String(i));
|
||||
return button;
|
||||
}
|
||||
|
||||
let buttonsCreated = false;
|
||||
|
||||
sendCommand('n');
|
||||
sendCommand('s');
|
||||
|
||||
setInterval(() => sendCommand('s'), 2000);
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
# Simple Flask backend to work with the JavaScript frontend for the antenna switcher by DL9UL
|
||||
# License: Apache 2
|
||||
# Copyright (c) 2024 Dimitar Milkov, LZ2DMV
|
||||
|
||||
# pip install flask flask-cors RPi.GPIO
|
||||
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
import RPi.GPIO as GPIO
|
||||
import os
|
||||
|
||||
config_file = 'antenna_switcher.cfg'
|
||||
antenna_file = 'ant'
|
||||
|
||||
def load_config():
|
||||
config = {}
|
||||
if os.path.exists(config_file):
|
||||
with open(config_file, 'r') as file:
|
||||
for line in file:
|
||||
name, value = line.strip().split('=')
|
||||
if name == 'antenna_pins':
|
||||
config[name] = eval(value)
|
||||
return config
|
||||
|
||||
config = load_config()
|
||||
antenna_pins = config.get('antenna_pins')
|
||||
|
||||
if antenna_pins is None:
|
||||
raise ValueError("Configuration file is missing required values.")
|
||||
|
||||
num_antennas = len(antenna_pins)
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
|
||||
for pin in antenna_pins:
|
||||
GPIO.setup(pin, GPIO.OUT)
|
||||
|
||||
def read_active_antenna():
|
||||
if os.path.exists(antenna_file):
|
||||
with open(antenna_file, 'r') as file:
|
||||
return file.read().strip()
|
||||
return None
|
||||
|
||||
def write_active_antenna(value):
|
||||
with open(antenna_file, 'w') as file:
|
||||
file.write(value)
|
||||
|
||||
def set_gpio_for_antenna(antenna_id):
|
||||
for i, pin in enumerate(antenna_pins):
|
||||
GPIO.output(pin, GPIO.HIGH if i == antenna_id else GPIO.LOW)
|
||||
|
||||
def initialize_antenna():
|
||||
active_antenna = read_active_antenna()
|
||||
if active_antenna and active_antenna.isdigit() and 1 <= int(active_antenna) <= num_antennas:
|
||||
set_gpio_for_antenna(int(active_antenna) - 1)
|
||||
else:
|
||||
set_gpio_for_antenna(0)
|
||||
write_active_antenna('1')
|
||||
|
||||
@app.route('/antenna_switch', methods=['POST'])
|
||||
def antennaswitch():
|
||||
data = request.get_json()
|
||||
command = data.get('command')
|
||||
|
||||
if command.isdigit() and 1 <= int(command) <= num_antennas:
|
||||
return set_antenna(command)
|
||||
elif command == 's':
|
||||
return get_active_antenna()
|
||||
elif command == 'n':
|
||||
return get_antenna_count()
|
||||
else:
|
||||
return jsonify({'error': 'Invalid command'}), 400
|
||||
|
||||
def get_active_antenna():
|
||||
active_antenna = read_active_antenna()
|
||||
if active_antenna is None:
|
||||
return jsonify(payload={'response': '0'})
|
||||
return jsonify(payload={'response': active_antenna})
|
||||
|
||||
def get_antenna_count():
|
||||
return jsonify(payload={'response': f'n:{num_antennas}'})
|
||||
|
||||
def set_antenna(antenna_id):
|
||||
active_antenna = read_active_antenna()
|
||||
if active_antenna == antenna_id:
|
||||
return jsonify(payload={'response': antenna_id})
|
||||
|
||||
try:
|
||||
set_gpio_for_antenna(int(antenna_id) - 1)
|
||||
write_active_antenna(antenna_id)
|
||||
return jsonify(payload={'response': antenna_id})
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return jsonify(payload={'response': '0'}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
initialize_antenna()
|
||||
app.run(host='127.0.0.1', port=8075)
|
||||
finally:
|
||||
GPIO.cleanup()
|
||||
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
# Install script for the antenna_switcher backend.
|
||||
# Assumes a Debian / Raspberry Pi OS / Ubuntu system with an APT
|
||||
# package manager and a OpenWebRX+ installation from the repository.
|
||||
#
|
||||
# You would need to adjust the script if you want to use it inside
|
||||
# a Docker container, but it is probably a bad idea anyway.
|
||||
#
|
||||
# License: Apache 2
|
||||
# Copyright (c) 2024 Dimitar Milkov, LZ2DMV
|
||||
|
||||
apt-get update
|
||||
apt-get install -y python3 python3-pip python3-venv nginx
|
||||
|
||||
# download backend
|
||||
mkdir -p /opt/antenna_switcher
|
||||
pushd /opt/antenna_switcher
|
||||
repo=https://raw.githubusercontent.com/0xAF/openwebrxplus-plugins/main/receiver/antenna_switcher
|
||||
wget --no-clobber -O antenna_switcher.py "$repo"/antenna_switcher.py
|
||||
wget --no-clobber -O antenna_switcher.cfg "$repo"/antenna_switcher.cfg
|
||||
echo "1" > ant
|
||||
# prepare venv
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install flask flask-cors RPi.GPIO
|
||||
deactivate
|
||||
popd
|
||||
|
||||
# systemd service
|
||||
cat << _EOF_ > /etc/systemd/system/antenna_switcher.service
|
||||
[Unit]
|
||||
Description=Antenna Switcher Backend
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/antenna_switcher
|
||||
ExecStart=/opt/antenna_switcher/venv/bin/python3 -m antenna_switcher
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
_EOF_
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now antenna_switcher
|
||||
|
||||
# nginx configuration
|
||||
mkdir -p /etc/nginx/snippets
|
||||
cat << _EOF_ > /etc/nginx/snippets/antenna_switcher.conf
|
||||
set \$antennaBackend 127.0.0.1:8075;
|
||||
location /antenna_switch {
|
||||
proxy_pass http://\$antennaBackend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
|
||||
# required for websockets
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection \$http_connection;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
}
|
||||
_EOF_
|
||||
|
||||
nginx_owrx_config="/etc/nginx/sites-available/openwebrx"
|
||||
|
||||
if [ -f "$nginx_owrx_config" ]; then
|
||||
# some black magic with sed to add include line in the end of the server block
|
||||
grep -qF 'include snippets/antenna_switcher.conf' $nginx_owrx_config || sed -ri.bak \
|
||||
':a;N;$!ba;s/}[\s\r\n]*$/\n\tinclude snippets\/antenna_switcher.conf;\n}/' \
|
||||
$nginx_owrx_config
|
||||
|
||||
systemctl restart varnish nginx
|
||||
else
|
||||
echo "You need to create your nginx site in /etc/nginx/sites-enabled and include snippets/antenna_switcher.conf."
|
||||
echo "Sample config [/etc/nginx/sites-enabled/openwebrx]:"
|
||||
cat << _EOF_
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
listen 443 ssl default_server;
|
||||
listen [::]:443 ssl default_server;
|
||||
gzip off;
|
||||
include snippets/snakeoil.conf;
|
||||
|
||||
set \$upstream 127.0.0.1:8073; # OWRX
|
||||
location / {
|
||||
proxy_pass http://\$upstream;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
|
||||
# required for websockets
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection \$http_connection;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
include snippets/antenna_switcher.conf;
|
||||
}
|
||||
_EOF_
|
||||
fi
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Colorful Spectrum"
|
||||
permalink: /receiver/colorful_spectrum
|
||||
---
|
||||
|
||||
This `receiver` plugin will colorify your spectrum analyzer.
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||
|
||||
## Load
|
||||
|
||||
Add this line in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/colorful_spectrum/colorful_spectrum.js');
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Plugin: colorify the spectrum analyzer.
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2023 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*/
|
||||
|
||||
// do not load CSS for this plugin
|
||||
Plugins.colorful_spectrum.no_css = true;
|
||||
|
||||
Plugins.colorful_spectrum.init = async function () {
|
||||
|
||||
// Check if utils plugin is loaded
|
||||
if (!Plugins.isLoaded('utils', 0.1)) {
|
||||
// try to load the utils plugin
|
||||
await Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/utils/utils.js');
|
||||
|
||||
// check again if it was loaded successfully
|
||||
if (!Plugins.isLoaded('utils', 0.1)) {
|
||||
console.error('colorful_spectrum plugin depends on "utils >= 0.1".');
|
||||
return false;
|
||||
} else {
|
||||
Plugins._debug('Plugin "utils" has been loaded as dependency.');
|
||||
}
|
||||
}
|
||||
|
||||
// wait for OWRX to initialize
|
||||
$(document).on('event:owrx_initialized', function () {
|
||||
Plugins.utils.wrap_func(
|
||||
'draw',
|
||||
function (orig, thisArg, args) {
|
||||
return true;
|
||||
},
|
||||
function (res, thisArg, args) {
|
||||
var vis_freq = get_visible_freq_range();
|
||||
var vis_start = 0.5 - (center_freq - vis_freq.start) / bandwidth;
|
||||
var vis_end = 0.5 - (center_freq - vis_freq.end) / bandwidth;
|
||||
var data_start = Math.round(fft_size * vis_start);
|
||||
var data_end = Math.round(fft_size * vis_end);
|
||||
var data_width = data_end - data_start;
|
||||
var data_height = Math.abs(thisArg.max - thisArg.min);
|
||||
var spec_width = thisArg.el.offsetWidth;
|
||||
var spec_height = thisArg.el.offsetHeight;
|
||||
if (spec_width <= data_width) {
|
||||
var x_ratio = data_width / spec_width;
|
||||
var y_ratio = spec_height / data_height;
|
||||
for (var x = 0; x < spec_width; x++) {
|
||||
var data = (thisArg.data[data_start + ((x * x_ratio) | 0)]);
|
||||
var y = (data - thisArg.min) * y_ratio;
|
||||
thisArg.ctx.fillRect(x, spec_height, 1, -y);
|
||||
if (data) {
|
||||
var c = Waterfall.makeColor(data);
|
||||
thisArg.ctx.fillStyle = "rgba(" +
|
||||
c[0] + ", " + c[1] + ", " + c[2] + ", " +
|
||||
(25 + y * 2) + "%)";
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
spectrum
|
||||
);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
After Width: | Height: | Size: 272 KiB |
29
docker/openwebrx/plugins/receiver/connect_notify/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Connect/Disconnect notifications"
|
||||
permalink: /receiver/connect_notify
|
||||
---
|
||||
|
||||
This `receiver` plugin will:
|
||||
|
||||
* Send a chat message to all users when you connect/disconnect to SDR
|
||||
* Show notification when another user is connected/disconnected to SDR
|
||||
|
||||
The plugin depends on [notify](https://0xaf.github.io/openwebrxplus-plugins/receiver/notify) plugin.
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||
|
||||
## Load
|
||||
|
||||
Add this line in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/notify/notify.js');
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/connect_notify/connect_notify.js');
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Plugin: User connect notification
|
||||
*
|
||||
* - Send a chat message to all users when you connect to SDR
|
||||
* - Show notification when another user is connected to SDR
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2023 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*/
|
||||
|
||||
// no css for this plugin
|
||||
Plugins.connect_notify.no_css = true;
|
||||
|
||||
// Initialize the plugin
|
||||
Plugins.connect_notify.init = async function () {
|
||||
|
||||
if (!Plugins.isLoaded('notify', 0.1)) {
|
||||
// try to load the notify plugin
|
||||
await Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/notify/notify.js');
|
||||
|
||||
// check again if it was loaded successfully
|
||||
if (!Plugins.isLoaded('notify', 0.1)) {
|
||||
console.error('connect_notify plugin depends on "notify >= 0.1".');
|
||||
return false;
|
||||
} else {
|
||||
Plugins._debug('Plugin "notify" has been loaded as dependency.');
|
||||
}
|
||||
}
|
||||
|
||||
Plugins.connect_notify.last = -1;
|
||||
$(document).on('server:clients:after', function (e, data) {
|
||||
var users = data - 1;
|
||||
if (Plugins.connect_notify.last < 0) {
|
||||
// this is our connection, so initialize.
|
||||
Plugins.connect_notify.last = users;
|
||||
// delay 100ms so the page initialize
|
||||
setTimeout(function () {
|
||||
var nick = LS.has('chatname') ? LS.loadStr('chatname') : 'Unknown';
|
||||
Chat.sendMessage('Connected.', nick);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
if (users != Plugins.connect_notify.last) {
|
||||
Plugins.notify.show('User ' + (
|
||||
(users > Plugins.connect_notify.last) ? 'Connected' : 'Disconnected'
|
||||
));
|
||||
Plugins.connect_notify.last = users;
|
||||
}
|
||||
});
|
||||
$(window).bind('beforeunload', function () {
|
||||
var nick = LS.has('chatname') ? LS.loadStr('chatname') : 'Unknown';
|
||||
Chat.sendMessage('Disconnected.', nick);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
After Width: | Height: | Size: 346 KiB |
34
docker/openwebrx/plugins/receiver/doppler/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Doppler"
|
||||
permalink: /receiver/doppler
|
||||
---
|
||||
|
||||
This `receiver` plugin will track the Doppler shift frequency of a chosen satellite. Useful for SSTV/Packet.
|
||||
|
||||
This plugin started as a port of [work](https://github.com/studentkra/OpenWebRX-Doppler) by [Sergey Osipov](https://github.com/studentkra).
|
||||
Then I switched to [CelesTrak JSON API](https://celestrak.org/) and created Satellite Finder modal window.
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
1. Open the Satellite finder window to choose a satellite or enter the SatID if you know it.
|
||||
2. Click TRACK.
|
||||
|
||||
The Satellite Finder window will help you find a satellite and will give useful information on each satellite.
|
||||

|
||||
|
||||
## Load
|
||||
|
||||
Add this line in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/doppler/doppler.js');
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
182
docker/openwebrx/plugins/receiver/doppler/doppler.css
Normal file
@@ -0,0 +1,182 @@
|
||||
:root {
|
||||
--satellite-bg: #333333;
|
||||
--satellite-fg: #ffffff;
|
||||
--satellite-bg-bars: #575757;
|
||||
--satellite-grad-1: #373737;
|
||||
--satellite-grad-2: #4F4F4F;
|
||||
}
|
||||
|
||||
body.has-theme {
|
||||
--satellite-bg: var(--theme-color1);
|
||||
--satellite-bg-bars: var(--theme-color2);
|
||||
--satellite-grad-1: var(--theme-gradient-color1);
|
||||
--satellite-grad-2: var(--theme-gradient-color2);
|
||||
}
|
||||
|
||||
.blocker {
|
||||
z-index: 2001 !important;
|
||||
}
|
||||
|
||||
.modal {
|
||||
z-index: 2002 !important;
|
||||
}
|
||||
|
||||
.modal.satellite-modal {
|
||||
height: 500px;
|
||||
background-color: var(--satellite-bg);
|
||||
color: var(--satellite-fg);
|
||||
padding: 2rem 0.5rem;
|
||||
}
|
||||
|
||||
.satellite-modal-header {
|
||||
background-color: var(--satellite-bg-bars);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-top-right-radius: 8px;
|
||||
border-top-left-radius: 8px;
|
||||
padding: 4px 10px;
|
||||
font-variant: small-caps;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.satellite-modal-body {
|
||||
position: absolute;
|
||||
top: 1.7rem;
|
||||
bottom: 2.3rem;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.satellite-modal-footer {
|
||||
background-color: var(--satellite-bg-bars);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
padding: 4px 10px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
|
||||
.satellite-modal-tabs-wrapper {
|
||||
max-width: 50rem;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.satellite-modal-tabs {
|
||||
position: relative;
|
||||
background-color: var(--satellite-bg-bars);
|
||||
/* background: linear-gradient(to bottom, var(--satellite-grad-1) 0%, var(--satellite-grad-2) 100%); */
|
||||
/* height: 14.75rem; */
|
||||
}
|
||||
|
||||
.satellite-modal-tabs::before,
|
||||
.satellite-modal-tabs::after {
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.satellite-modal-tabs::after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.satellite-modal-tab {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.satellite-modal-tab-switch {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.satellite-modal-tab-label {
|
||||
position: relative;
|
||||
display: block;
|
||||
line-height: 1.75em;
|
||||
height: 2em;
|
||||
padding: 0 .6em;
|
||||
/* background: linear-gradient(to bottom, var(--satellite-grad-1) 0%, var(--satellite-grad-2) 100%); */
|
||||
background: var(--satellite-bg-bars);
|
||||
border-right: 0.125rem solid var(--satellite-bg);
|
||||
color: var(--satellite-fg);
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.satellite-modal-tab-label:hover {
|
||||
border-top: var(--satellite-bg) solid 1px;
|
||||
top: -0.25rem;
|
||||
transition: top 0.25s;
|
||||
}
|
||||
|
||||
.satellite-modal-tab-content {
|
||||
height: 12rem;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 2em;
|
||||
left: 0;
|
||||
padding: .25rem .25rem;
|
||||
background: linear-gradient(to bottom, var(--satellite-grad-1) 0%, var(--satellite-grad-2) 100%);
|
||||
color: var(--satellite-fg);
|
||||
/* border-bottom: 0.25rem solid var(--satellite-bg); */
|
||||
opacity: 0;
|
||||
transition: all 0.35s;
|
||||
height: 404px;
|
||||
width: calc(100% - .5rem);
|
||||
}
|
||||
|
||||
.satellite-modal-tab-switch:checked+.satellite-modal-tab-label {
|
||||
background: linear-gradient(to bottom, var(--satellite-grad-1) 0%, var(--satellite-grad-2) 100%);
|
||||
color: var(--satellite-fg);
|
||||
border-bottom: 0;
|
||||
border-right: 0.125rem solid var(--satellite-bg);
|
||||
transition: all 0.35s;
|
||||
z-index: 1;
|
||||
top: -0.0625rem;
|
||||
}
|
||||
|
||||
.satellite-modal-tab-switch:checked+label+.satellite-modal-tab-content {
|
||||
z-index: 2;
|
||||
opacity: 1;
|
||||
transition: all 0.35s;
|
||||
}
|
||||
|
||||
.satellite-table {
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.satellite-table>tbody>tr:hover {
|
||||
background-color: var(--satellite-bg-bars);
|
||||
filter: drop-shadow(3px 3px 10px black);
|
||||
}
|
||||
|
||||
#satellite-name {
|
||||
border: 0px solid;
|
||||
width: 126px;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
height: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#satellite-row {
|
||||
padding: 4px 0px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#satellite-input {
|
||||
width: 54px;
|
||||
}
|
||||
|
||||
#satellite-track {
|
||||
width: 48px;
|
||||
text-align: center;
|
||||
}
|
||||
450
docker/openwebrx/plugins/receiver/doppler/doppler.js
Normal file
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Plugin: Doppler - Track satellite frequency (Doppler shift/effect)
|
||||
*
|
||||
* This plugin started as a port of Sergey Osipov work:
|
||||
* https://github.com/studentkra/OpenWebRX-Doppler
|
||||
* Then evolved.
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2024 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*
|
||||
* TODO:
|
||||
* - Option to integrate sat bookmarks
|
||||
* - Associate the bookmarks with modulation and SatID so it can be easily tracked, once bookmark is clicked.
|
||||
* - Scan the LocalStorage and remove old groups.
|
||||
*/
|
||||
|
||||
|
||||
// no css for this plugin
|
||||
// Plugins.doppler.no_css = true;
|
||||
|
||||
// Initialize the plugin
|
||||
Plugins.doppler.init = async function () {
|
||||
// Check if utils plugin is loaded
|
||||
// if (!Plugins.isLoaded('utils', 0.3)) {
|
||||
// console.error('Example plugin depends on "utils >= 0.3".');
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// await Plugins._load_script('http://192.168.175.99:8080/doppler/sat.js').catch(function () {
|
||||
await Plugins._load_script('https://0xaf.github.io/openwebrxplus-plugins/receiver/doppler/sat.js').catch(function() {
|
||||
throw ("Cannot load satellite-js script.");
|
||||
});
|
||||
|
||||
await Plugins._load_script('https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.5.0/lz-string.min.js').catch(function () {
|
||||
throw ("Cannot load lz-string script.");
|
||||
});
|
||||
|
||||
|
||||
await Plugins._load_style('https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.css').catch(function () {
|
||||
throw ("Cannot load jquery-modal style.");
|
||||
});
|
||||
|
||||
await Plugins._load_script('https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.js').catch(function () {
|
||||
throw ("Cannot load jquery-modal script.");
|
||||
}).then(() => {
|
||||
// $.modal.defaults.escapeClose = true;
|
||||
// $.modal.defaults.clickClose = false;
|
||||
// $.modal.defaults.showClose = false;
|
||||
});
|
||||
|
||||
// initialize on load
|
||||
if ($("#satellite-row").length < 1) {
|
||||
$(".openwebrx-modes").after(`
|
||||
<div id="satellite-row" class="openwebrx-panel-line openwebrx-panel-flex-line">
|
||||
<input id="satellite-input" type="text" placeholder="Sat ID">
|
||||
<div id="satellite-name" class="openwebrx-button">Open SAT Finder</div>
|
||||
<div id="satellite-track" class="openwebrx-button">TRACK</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
var modalTabs = `
|
||||
<div class="satellite-modal-tabs-wrapper">
|
||||
<div class="satellite-modal-tabs">
|
||||
`;
|
||||
|
||||
var groups = Object.keys(Plugins.doppler.satelliteGroups);
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
modalTabs += `
|
||||
<div class="satellite-modal-tab">
|
||||
<input type="radio" name="css-tabs" id="satellite-tab-${i}" ${i == 0 ? 'xxx-checked' : ''} onclick="Plugins.doppler.tabChange(${i}, '${groups[i]}')" class="satellite-modal-tab-switch" data-group="${groups[i]}">
|
||||
<label for="satellite-tab-${i}" class="satellite-modal-tab-label">${groups[i]}</label>
|
||||
<div class="satellite-modal-tab-content" id="satellite-tab-content-${i}">
|
||||
<div class="openwebrx-panel" style="transform: none; padding:0; background: none;">
|
||||
<select id="satellite-tab-content-${i}-select" class="openwebrx-panel-listbox" style="width: 70%; text-align: center" onchange="Plugins.doppler.selectChange(${i})">
|
||||
</select>
|
||||
<label style="">
|
||||
Above <input id="satellite-tab-content-${i}-elevation" style="width: 8%" type="number" value="20" onchange="Plugins.doppler.selectChange(${i})" title="Find satellites above X degrees elevation"> °
|
||||
</label>
|
||||
<div id="satellite-tab-content-${i}-refresh" class="openwebrx-button" style="float: right;" onclick="Plugins.doppler.toggleRefresh(${i})" title="Refresh every 5sec">
|
||||
<svg width="16px" height="16px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke="#FFFFFF" stroke-width="3" stroke-linecap="round"
|
||||
stroke-linejoin="round" d="M18.6091 5.89092L15.5 9H21.5V3L18.6091 5.89092ZM18.6091 5.89092C16.965 4.1131 14.6125 3 12 3C7.36745 3 3.55237 6.50005 3.05493 11M5.39092 18.1091L2.5 21V15H8.5L5.39092 18.1091ZM5.39092 18.1091C7.03504 19.8869 9.38753 21 12 21C16.6326 21 20.4476 17.5 20.9451 13"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div id="satellite-tab-content-${i}-list" style="height: 370px; overflow-y: scroll">
|
||||
<table class="satellite-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">SatID</th>
|
||||
<th align="center">Name</th>
|
||||
<th align="center">Elev</th>
|
||||
<th align="center">Az</th>
|
||||
<th align="center">Dist</th>
|
||||
<th align="right">Visible</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="satellite-tab-content-${i}-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
modalTabs += `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('#satellite-row').append(`
|
||||
<div id="satellite-modal" class="modal satellite-modal">
|
||||
<div class="satellite-modal-header">
|
||||
Satellite Finder (up to 2 hours)
|
||||
</div>
|
||||
<div class="satellite-modal-body">
|
||||
${modalTabs}
|
||||
<br><br><center style="vertical-align: middle">Select Category</center>
|
||||
</div>
|
||||
<div class="satellite-modal-footer">
|
||||
<div class="openwebrx-button" rel="modal:close" onclick="$.modal.close()">Close</div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
$('#satellite-modal').on($.modal.BEFORE_CLOSE, function(event, modal) {
|
||||
if (Plugins.doppler.scanRunning !== undefined) {
|
||||
Plugins.doppler.toggleRefresh(Plugins.doppler.scanRunning);
|
||||
}
|
||||
});
|
||||
|
||||
$("#satellite-name").click(() => {
|
||||
// window.open("https://tle.ivanstanojevic.me/", "_blank");
|
||||
$('#satellite-modal').modal({
|
||||
escapeClose: true,
|
||||
clickClose: false,
|
||||
showClose: false,
|
||||
});
|
||||
});
|
||||
|
||||
$("#satellite-track").click(() => {
|
||||
if (Plugins.doppler.intervalId) {
|
||||
Plugins.doppler.stop_tracker();
|
||||
return;
|
||||
}
|
||||
|
||||
if (($('#satellite-input').val()).length < 1) {
|
||||
Plugins.doppler.stop_tracker("NOT FOUND!");
|
||||
return;
|
||||
}
|
||||
|
||||
var satObj = null;
|
||||
if (Plugins.doppler.lastGroupName) {
|
||||
try {
|
||||
var store = JSON.parse(LZString.decompress(LS.loadStr('satellites.' + Plugins.doppler.lastGroupName)));
|
||||
satObj = store.data.find(obj => obj.NORAD_CAT_ID === parseInt($('#satellite-input').val() ,10));
|
||||
Plugins.doppler.start_tracker(satObj);
|
||||
} catch (e) { satObj = null; }
|
||||
}
|
||||
|
||||
if (!satObj) {
|
||||
fetch('https://celestrak.org/NORAD/elements/gp.php?CATNR=' + $('#satellite-input').val() + '&FORMAT=JSON')
|
||||
.then(response => response.json())
|
||||
.then(data2 => {
|
||||
Plugins.doppler.start_tracker(data2[0])
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
Plugins.doppler.stop_tracker(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
} // initialize
|
||||
|
||||
return true; // plugin init return
|
||||
}
|
||||
|
||||
Plugins.doppler.stop_tracker = function (info) {
|
||||
clearInterval(Plugins.doppler.intervalId);
|
||||
Plugins.doppler.intervalId = undefined;
|
||||
$('#satellite-track').removeClass('highlighted').text('TRACK');
|
||||
$('#satellite-name').text((info && info.length) ? info : "Open SAT Finder");
|
||||
}
|
||||
|
||||
Plugins.doppler.start_tracker = function (obj) {
|
||||
// var satrec = satellite.twoline2satrec(line1, line2);
|
||||
// var satrec = satellite.object2satrec(obj);
|
||||
var satrec = satellite.json2satrec(obj);
|
||||
if (satrec.error>0) {
|
||||
Plugins.doppler.stop_tracker("Bad SAT Data");
|
||||
return;
|
||||
};
|
||||
var asl = $('.webrx-rx-desc').text().match(/ASL:\s*(\d+)\s*m/);
|
||||
asl = (asl && asl[1] && parseInt(asl[1]) > 0) ? parseInt(asl[1]) / 1000 : 0;
|
||||
var receiverPos = Utils.getReceiverPos();
|
||||
if (!receiverPos || !receiverPos.lat || !receiverPos.lon) {
|
||||
Plugins.doppler.stop_tracker("Set Receiver Position");
|
||||
return;
|
||||
}
|
||||
|
||||
$("#satellite-name").text(obj.OBJECT_NAME);
|
||||
$("#satellite-track").addClass('highlighted').text("STOP");
|
||||
var demodulator = $('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator();
|
||||
var startFreq = demodulator.get_offset_frequency() + center_freq;
|
||||
Plugins.doppler.intervalId = setInterval(() => {
|
||||
var demodulator = $('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator();
|
||||
newFreq = Plugins.doppler.getDoppler(satrec, asl, receiverPos.lat, receiverPos.lon, startFreq);
|
||||
demodulator.set_offset_frequency(newFreq - center_freq);
|
||||
console.debug(`Doppler Freq: ${newFreq}`);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
Plugins.doppler.getDoppler = function (satrec, asl, lat, lon, center) {
|
||||
var positionAndVelocity = satellite.propagate(satrec, new Date());
|
||||
var positionEci = positionAndVelocity.position;
|
||||
var velocityEci = positionAndVelocity.velocity;
|
||||
var observerGd = {
|
||||
longitude: satellite.degreesToRadians(lon),
|
||||
latitude: satellite.degreesToRadians(lat),
|
||||
height: asl
|
||||
};
|
||||
var gmst = satellite.gstime(new Date());
|
||||
var positionEcf = satellite.eciToEcf(positionEci, gmst);
|
||||
var observerEcf = satellite.geodeticToEcf(observerGd);
|
||||
var velocityEcf = satellite.eciToEcf(velocityEci, gmst);
|
||||
var dopplerFactor = satellite.dopplerFactor(observerEcf, positionEcf, velocityEcf);
|
||||
return (Math.round(dopplerFactor * center));
|
||||
}
|
||||
|
||||
Plugins.doppler.tabChange = function (id, tab) {
|
||||
var options = `<option value="--empty--">-- Select group --`;
|
||||
for (const [key, val] of Object.entries(Plugins.doppler.satelliteGroups[tab])) {
|
||||
options += `<option value="${val}">${key}`;
|
||||
}
|
||||
var opts = $('#satellite-tab-content-' + id + '-select');
|
||||
opts.empty().append(`${options}`);
|
||||
}
|
||||
|
||||
Plugins.doppler.selectChange = async function (id) {
|
||||
var groupName = $('#satellite-tab-content-' + id + '-select').val();
|
||||
if (!groupName.length || groupName === '--empty--') { console.error('no sat group selected'); return; }
|
||||
var elev = parseInt($('#satellite-tab-content-' + id +'-elevation').val(), 10);
|
||||
if (isNaN(elev) || elev < 1) { console.error('bad elevation: '+elev); return; }
|
||||
|
||||
var store;
|
||||
try { store = JSON.parse(LZString.decompress(LS.loadStr('satellites.' + groupName))); }
|
||||
catch (e) { store = null; }
|
||||
|
||||
// if we don't have cached data, or it is more than 2h old, get new data
|
||||
if (store === null || ((Math.floor(Date.now() / 1000) - store.last_sync) > (2 * 60 * 60))) {
|
||||
await fetch('https://celestrak.org/NORAD/elements/gp.php?GROUP=' + groupName + '&FORMAT=JSON')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
store = {};
|
||||
store.last_sync = Math.floor(Date.now() / 1000);
|
||||
store.data = data;
|
||||
LS.save('satellites.' + groupName, LZString.compress(JSON.stringify(store)));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
var asl = $('.webrx-rx-desc').text().match(/ASL:\s*(\d+)\s*m/);
|
||||
asl = (asl && asl[1] && parseInt(asl[1]) > 0) ? parseInt(asl[1]) / 1000 : 0;
|
||||
var receiverPos = Utils.getReceiverPos();
|
||||
var observerGd = (receiverPos && receiverPos.lat && receiverPos.lon) ? {
|
||||
longitude: satellite.degreesToRadians(receiverPos.lon),
|
||||
latitude: satellite.degreesToRadians(receiverPos.lat),
|
||||
height: asl
|
||||
} : null;
|
||||
|
||||
|
||||
for (let i = 0; i < store.data.length; i++) {
|
||||
if (observerGd === null) {
|
||||
store.data[i].next_pass = 'Receiver position unknown.';
|
||||
break;
|
||||
}
|
||||
|
||||
// lets find if the sat will be visible in the next hour
|
||||
// https://github.com/shashwatak/satellite-js/issues/56
|
||||
let curDate = new Date();
|
||||
// let satrec = satellite.object2satrec(store.data[i]);
|
||||
let satrec = satellite.json2satrec(store.data[i]);
|
||||
for (let m = 0; m < 120; m++) {
|
||||
// check every minute for the next hour
|
||||
var positionAndVelocity = satellite.propagate(satrec, curDate);
|
||||
if (satrec.error > 0) {
|
||||
console.error("Cant propagate. Bad satrec for " + store.data[i].OBJECT_NAME);
|
||||
console.log(store.data[i]);
|
||||
console.log(satrec);
|
||||
break;
|
||||
}
|
||||
var gmst = satellite.gstime(curDate);
|
||||
// var gmst = satellite.gstime(new Date());
|
||||
var positionEci = positionAndVelocity.position;
|
||||
var positionEcf = satellite.eciToEcf(positionEci, gmst);
|
||||
var lookAngles = satellite.ecfToLookAngles(observerGd, positionEcf);
|
||||
store.data[i].elevation = satellite.radiansToDegrees(lookAngles.elevation);
|
||||
store.data[i].azimuth = satellite.radiansToDegrees(lookAngles.azimuth);
|
||||
store.data[i].distance = lookAngles.rangeSat;
|
||||
if (store.data[i].elevation > elev) {
|
||||
store.data[i].next_pass = curDate;
|
||||
break;
|
||||
}
|
||||
curDate.setMinutes(curDate.getMinutes() + 1); // add one minute
|
||||
}
|
||||
}
|
||||
|
||||
store.data.sort(function(a, b) {
|
||||
if ((!a.next_pass || !(a.next_pass instanceof Date)) && (!b.next_pass || !(b.next_pass instanceof Date))) return 0;
|
||||
if (!a.next_pass || !(a.next_pass instanceof Date)) return 1;
|
||||
if (!b.next_pass || !(b.next_pass instanceof Date)) return -1;
|
||||
return a.next_pass - b.next_pass;
|
||||
});
|
||||
|
||||
var tableRows;
|
||||
for (let i = 0; i < store.data.length; i++) {
|
||||
const s = store.data[i];
|
||||
let vis = ">2h";
|
||||
if (s.next_pass) {
|
||||
if (s.next_pass <= new Date()) {
|
||||
vis = "<b>*NOW*</b>";
|
||||
} else {
|
||||
vis = String(s.next_pass.getHours()).padStart(2, '0') + ":" + String(s.next_pass.getMinutes()).padStart(2, '0');
|
||||
}
|
||||
}
|
||||
tableRows += `
|
||||
<tr onclick="Plugins.doppler.selectSatellite(${s.NORAD_CAT_ID}, '${groupName}')">
|
||||
<td align="left">${s.NORAD_CAT_ID}</td>
|
||||
<td align="center" style="max-width:160px; width: 160px;">${s.OBJECT_NAME}</td>
|
||||
<td align="center">${Math.round(s.elevation)}°</td>
|
||||
<td align="center">${Math.round(s.azimuth)}°</td>
|
||||
<td align="center">${Math.round(s.distance)}km</td>
|
||||
<td align="right">${vis}</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
$('#satellite-tab-content-' + id + '-tbody').empty().append(`${tableRows}`);
|
||||
}
|
||||
|
||||
Plugins.doppler.selectSatellite = function (id, grp) {
|
||||
Plugins.doppler.lastGroupName = grp;
|
||||
Plugins.doppler.lastSatId = id;
|
||||
$('#satellite-input').val(id);
|
||||
if (Plugins.doppler.intervalId) Plugins.doppler.stop_tracker();
|
||||
$.modal.close();
|
||||
}
|
||||
|
||||
Plugins.doppler.toggleRefresh = function (id) {
|
||||
const refresh = $('#satellite-tab-content-' + id + '-refresh');
|
||||
if (Plugins.doppler.scanRunning === undefined) {
|
||||
refresh.css({ animationName: 'openwebrx-scan-animation', animationDuration: '1s', animationIterationCount: 'infinite', filter: 'none'});
|
||||
Plugins.doppler.scanRunning = id;
|
||||
Plugins.doppler.selectChange(id);
|
||||
Plugins.doppler.scanIntervalId = setInterval(() => {
|
||||
Plugins.doppler.selectChange(Plugins.doppler.scanRunning);
|
||||
}, 5000);
|
||||
} else {
|
||||
if (Plugins.doppler.scanRunning !== id) { // another scan is running
|
||||
Plugins.doppler.toggleRefresh(Plugins.doppler.scanRunning);
|
||||
} else {
|
||||
clearInterval(Plugins.doppler.scanIntervalId);
|
||||
Plugins.doppler.scanIntervalId = undefined;
|
||||
Plugins.doppler.scanRunning = undefined;
|
||||
refresh.css({ animationName: ''});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cSpell:disable
|
||||
// Groups: https://celestrak.org/NORAD/elements/index.php?FORMAT=json
|
||||
Plugins.doppler.satelliteGroups = {
|
||||
'HAM': { // our custom set
|
||||
'Space Stations': 'stations',
|
||||
'Amateur Radio': 'amateur',
|
||||
'Weather': 'weather',
|
||||
'NOAA': 'noaa',
|
||||
},
|
||||
'Special': {
|
||||
'Last 30 Days\' Launches': 'last-30-days',
|
||||
'Space Stations': 'stations',
|
||||
'100 (or so) Brightest': 'visual',
|
||||
'Active Satellites': 'active',
|
||||
'Analyst Satellites': 'analyst',
|
||||
'Russian ASAT Test Debris (COSMOS 1408)': 'cosmos-1408-debris',
|
||||
'Chinese ASAT Test Debris (FENGYUN 1C)': 'fengyun-1c-debris',
|
||||
'IRIDIUM 33 Debris': 'iridium-33-debris',
|
||||
'COSMOS 2251 Debris': 'cosmos-2251-debris',
|
||||
},
|
||||
'Weather': {
|
||||
'Weather': 'weather',
|
||||
'NOAA': 'noaa',
|
||||
'GOES': 'goes',
|
||||
'Earth Resources': 'resource',
|
||||
'Search & Rescue (SARSAT)': 'sarsat',
|
||||
'Disaster Monitoring': 'dmc',
|
||||
'Tracking and Data Relay Satellite System (TDRSS)': 'tdrss',
|
||||
'ARGOS Data Collection System': 'argos',
|
||||
'Planet': 'planet',
|
||||
'Spire': 'spire',
|
||||
},
|
||||
'Comms': {
|
||||
'Active Geosynchronous': 'geo',
|
||||
'GEO Protected Zone': 'gpz',
|
||||
'GEO Protected Zone Plus': 'gpz-plus',
|
||||
'Intelsat': 'intelsat',
|
||||
'SES': 'ses',
|
||||
'Eutelsat': 'eutelsat',
|
||||
'Iridium': 'iridium',
|
||||
'Iridium NEXT': 'iridium-NEXT',
|
||||
'Starlink': 'starlink',
|
||||
'OneWeb': 'oneweb',
|
||||
'Orbcomm': 'orbcomm',
|
||||
'Globalstar': 'globalstar',
|
||||
'Swarm': 'swarm',
|
||||
'Amateur Radio': 'amateur',
|
||||
'SatNOGS': 'satnogs',
|
||||
'Experimental Comm': 'x-comm',
|
||||
'Other Comm': 'other-comm',
|
||||
'Gorizont': 'gorizont',
|
||||
'Raduga': 'raduga',
|
||||
'Molniya': 'molniya',
|
||||
},
|
||||
'Nav': {
|
||||
'GNSS': 'gnss',
|
||||
'GPS Operational': 'gps-ops',
|
||||
'GLONASS Operational': 'glo-ops',
|
||||
'Galileo': 'galileo',
|
||||
'Beidou': 'beidou',
|
||||
'Satellite-Based Augmentation System (WAAS/EGNOS/MSAS)': 'sbas',
|
||||
'Navy Navigation Satellite System (NNSS)': 'nnss',
|
||||
'Russian LEO Navigation': 'musson',
|
||||
},
|
||||
'Science': {
|
||||
'Space & Earth Science': 'science',
|
||||
'Geodetic': 'geodetic',
|
||||
'Engineering': 'engineering',
|
||||
'Education': 'education',
|
||||
},
|
||||
'Misc': {
|
||||
'Miscellaneous Military': 'military',
|
||||
'Radar Calibration': 'radar',
|
||||
'CubeSats': 'cubesat',
|
||||
'Other Satellites': 'other',
|
||||
},
|
||||
};
|
||||
// cSpell:enable
|
||||
BIN
docker/openwebrx/plugins/receiver/doppler/doppler.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
docker/openwebrx/plugins/receiver/doppler/doppler1.png
Normal file
|
After Width: | Height: | Size: 798 KiB |
3263
docker/openwebrx/plugins/receiver/doppler/sat.js
Normal file
12
docker/openwebrx/plugins/receiver/example/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Example plugin (for devs)"
|
||||
permalink: /receiver/example
|
||||
---
|
||||
|
||||
This `receiver` plugin is an example for other plugin developers.
|
||||
**There is no point in enabling this plugin.**
|
||||
|
||||
### Code
|
||||
|
||||
Code is in my [Github repo](https://github.com/0xAF/openwebrxplus-plugins/tree/main/receiver/example)
|
||||
153
docker/openwebrx/plugins/receiver/example/example.js
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* example Receiver Plugin for OpenWebRx+
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2023 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*/
|
||||
|
||||
// Disable CSS loading for this plugin
|
||||
Plugins.example.no_css = true;
|
||||
|
||||
// remove the next line if you really want to use this plugin
|
||||
throw ("This is the example plugin. It is not made for real world use.");
|
||||
|
||||
// Init function of the plugin
|
||||
Plugins.example.init = function () {
|
||||
|
||||
// Check if utils plugin is loaded
|
||||
if (!Plugins.isLoaded('utils', 0.1)) {
|
||||
console.error('Example plugin depends on "utils >= 0.1".');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Listen to profile change and print the new profile name to console.
|
||||
// NOTE: you cannot manipulate the data in events, you will need to wrap the original
|
||||
// function if you want to manipulate data.
|
||||
$(document).on('event:profile_changed', function (e, data) {
|
||||
console.log('profile change event: ' + data);
|
||||
});
|
||||
|
||||
// Another events:
|
||||
// event:owrx_initialized - called when OWRX is initialized
|
||||
|
||||
// Server events are triggered when server sends data over the WS
|
||||
// All server events have suffix ':before' or ':after', based on the original function call.
|
||||
// :before events are before the original function call,
|
||||
// :after events are after the original function call.
|
||||
// Some interesting server events:
|
||||
// server:config - server configuration
|
||||
// server:bookmarks - the bookmarks from server
|
||||
// server:clients - clients number change
|
||||
// server:profiles - sdr profiles
|
||||
// server:features - supported features
|
||||
|
||||
|
||||
// Modify an existing OWRX function with utils plugin.
|
||||
// See utils.js for documentation on wrap method.
|
||||
// This will wrap profile changing function
|
||||
Plugins.utils.wrap_func(
|
||||
// function to wrap around
|
||||
'sdr_profile_changed',
|
||||
|
||||
// before callback, to be run before the original function
|
||||
// orig = original function
|
||||
// thisArg = thisArg for the original function
|
||||
// args = the arguments for the original function
|
||||
// If you call the original function here (in the before_cb), always return false,
|
||||
// so the wrap_func() will not call it later again.
|
||||
// example of calling the original function: orig.apply(thisArg, args);
|
||||
function (orig, thisArg, args) {
|
||||
console.log("Before callback for: " + orig.name);
|
||||
|
||||
// check if newly selected profile is the PMR profile
|
||||
if ($('#openwebrx-sdr-profiles-listbox').find(':selected').text() === "[RTL] 446 PMR") {
|
||||
// prevent changing to this profile
|
||||
console.log('This profile is disabled by proxy function');
|
||||
|
||||
// restore the previous selected profile
|
||||
$('#openwebrx-sdr-profiles-listbox').val(currentprofile.toString());
|
||||
|
||||
// return false to prevent execution of original function
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true to allow execution of original function
|
||||
return true;
|
||||
},
|
||||
|
||||
// after callback, to be run after the original function,
|
||||
// but only if the before callback returns true
|
||||
// res = result of the original function, if any
|
||||
function (res) {
|
||||
console.log('profile changed.');
|
||||
}
|
||||
);
|
||||
|
||||
// this example will do the same (stop profile changing), but using another method
|
||||
// replace the "onchange" handler of the profiles selectbox
|
||||
// and call the original function "sdr_profile_changed"
|
||||
$('#openwebrx-sdr-profiles-listbox')[0].onchange = function (e) {
|
||||
|
||||
// check the index of the selected profile (0 is the first profile in the list)
|
||||
if (e.target.options.selectedIndex === 0) {
|
||||
// prevent changing to this profile
|
||||
console.log('This profile is disabled by onchange.');
|
||||
|
||||
// restore the previous profile
|
||||
$('#openwebrx-sdr-profiles-listbox').val(currentprofile.toString());
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, call the original function
|
||||
sdr_profile_changed();
|
||||
};
|
||||
|
||||
// this example will manipulate bookmarks data when the server sends the bookmarks
|
||||
// We will wrap the bookmarks.replace_bookmarks() function, once OWRX is initialized.
|
||||
// We cannot wrap the replace_bookmarks() function before the bookmarks object is created.
|
||||
// So we wait for OWRX to initialize and then wrap the function.
|
||||
$(document).on('event:owrx_initialized', function () {
|
||||
|
||||
// Call the wrap method of utils plugin
|
||||
Plugins.utils.wrap_func(
|
||||
|
||||
// function to wrap
|
||||
'replace_bookmarks',
|
||||
|
||||
// before callback
|
||||
function (orig, thisArg, args) {
|
||||
|
||||
// check if the bookmarks are "server bookmarks"
|
||||
if (args[1] === 'server') {
|
||||
|
||||
// check if we have array of bookmarks (will be empty if the profile has no bookmarks to show)
|
||||
if (typeof (args[0]) === 'object' && args[0].length)
|
||||
|
||||
// replace the name of the first bookmark
|
||||
args[0][0].name = 'replaced';
|
||||
}
|
||||
|
||||
// now call the original function
|
||||
orig.apply(thisArg, args);
|
||||
|
||||
// and return false, so the wrap_func() will not call the original for second time
|
||||
return false;
|
||||
},
|
||||
|
||||
// after callback
|
||||
function (res) {
|
||||
/* not executed because the before function returns false always */
|
||||
},
|
||||
|
||||
// this is the object, where the replace_bookmarks() function should be found
|
||||
bookmarks
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// return true for plugin init()
|
||||
return true;
|
||||
} // end of init function
|
||||
11
docker/openwebrx/plugins/receiver/example_theme/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Example Theme plugin (for devs)"
|
||||
permalink: /receiver/example_theme
|
||||
---
|
||||
|
||||
This `receiver` plugin is an example for other plugin/theme developers.
|
||||
**There is no point in enabling this plugin.**
|
||||
|
||||
### Code
|
||||
Code is in my [Github repo](https://github.com/0xAF/openwebrxplus-plugins/tree/main/receiver/example_theme)
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* colors for the new theme
|
||||
*/
|
||||
body.theme-eye-piercer {
|
||||
--theme-color1: #ff6262;
|
||||
--theme-color2: #ff626252;
|
||||
--theme-gradient-color1: #ff6262;
|
||||
--theme-gradient-color2: #ff0000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* example plugin, creating a new theme for OpenWebRx+
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2023 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*/
|
||||
|
||||
// Add new entry in the Theme selectbox
|
||||
$('#openwebrx-themes-listbox').append(
|
||||
$('<option>').val(
|
||||
// give it a value. you will need this for the css styles
|
||||
"eye-piercer"
|
||||
).text(
|
||||
// lets name it
|
||||
'Eye-Piercer'
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Frequency Far Jump"
|
||||
permalink: /receiver/frequency_far_jump
|
||||
---
|
||||
|
||||
This a simple `receiver` plugin to allow jumping to a frequency outside the boundary of the currently selected profile, by typing it in the receiver's frequency dial.
|
||||
|
||||
**Beware of the limitations of this approach:** the modulation and the other settings of the receiver **will stay the same** when jumping to the new, far frequency.
|
||||
|
||||
Please note that you **must** enable *"Allow users to change center frequency"* and in case you have set a **magic key**, you will have to provide it with a '*#key=[KEY]*' at the end of the URL.
|
||||
|
||||
The plugin depends on [utils](https://0xaf.github.io/openwebrxplus-plugins/receiver/utils) plugin.
|
||||
|
||||
## Load
|
||||
|
||||
Add this lines in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/utils/utils.js').then(async function () {
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/frequency_far_jump/frequency_far_jump.js');
|
||||
});
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Plugin: Jump to a frequency outside the boundaries of the selected profile by typing it in the receiver's frequency dial.
|
||||
* Requires the option 'Allow users to change center frequency' to be enabled in the admin panel.
|
||||
* Beware of the limitations of this approach: the modulation and the other settings of the receiver will stay the same when jumping to the new, far frequency.
|
||||
*
|
||||
* Please note that you must supply the magic key for your OpenWebRX+ instance if you have one configured with a '#key=[KEY]' at the end of the URL.
|
||||
*
|
||||
* License: Apache License 2.0
|
||||
* Copyright (c) 2024 Dimitar Milkov, LZ2DMV
|
||||
*/
|
||||
|
||||
Plugins.frequency_far_jump.no_css = true;
|
||||
|
||||
Plugins.frequency_far_jump.init = async function () {
|
||||
|
||||
if (!Plugins.isLoaded('utils', 0.1)) {
|
||||
|
||||
await Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/utils/utils.js');
|
||||
|
||||
if (!Plugins.isLoaded('utils', 0.1)) {
|
||||
console.error('Plugin "frequency_far_jump" depends on "utils >= 0.1".');
|
||||
return false;
|
||||
} else {
|
||||
Plugins._debug('Plugin "utils" has been loaded as dependency.');
|
||||
}
|
||||
}
|
||||
|
||||
Plugins.utils.wrap_func(
|
||||
'set_offset_frequency',
|
||||
function (orig, thisArg, args) {
|
||||
var to_what = Math.round(args[0]);
|
||||
|
||||
if (typeof(to_what) == 'undefined') return;
|
||||
|
||||
// The frequency is outside the boundaries of the current profile
|
||||
if (to_what > bandwidth / 2 || to_what < -bandwidth / 2) {
|
||||
// to_what is an offset, so we need to add the current full frequency (center_freq) to it
|
||||
var f = center_freq + to_what;
|
||||
var k = $('#openwebrx-panel-receiver').demodulatorPanel().getMagicKey();
|
||||
|
||||
// Ask the backend over the WS to switch the frequency for us
|
||||
ws.send(JSON.stringify({
|
||||
"type": "setfrequency", "params": { "frequency": f, "key": k }
|
||||
}));
|
||||
|
||||
} else {
|
||||
// The frequency is within the boundaries of the current profile,
|
||||
// just use the original set_offset_frequency
|
||||
orig.apply(thisArg, args);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
null,
|
||||
Demodulator.prototype
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
23
docker/openwebrx/plugins/receiver/init.js
Normal file
@@ -0,0 +1,23 @@
|
||||
// Receiver plugins initialization.
|
||||
// everything after '//' is a comment.
|
||||
|
||||
// uncomment the next line to enable plugin debugging in browser console.
|
||||
// Plugins._enable_debug = true;
|
||||
|
||||
// base URL for receiver plugins
|
||||
const rp_url = 'https://0xaf.github.io/openwebrxplus-plugins/receiver';
|
||||
|
||||
// First load the utils, needed for some plugins
|
||||
Plugins.load('utils').then(async function () {
|
||||
|
||||
// Load the notification plugin, used by some plugins. await to ensure it is loaded before the rest.
|
||||
await Plugins.load('notify');
|
||||
|
||||
// load remote plugins
|
||||
Plugins.load('colorful_spectrum');
|
||||
Plugins.load('doppler');
|
||||
Plugins.load('frequency_far_jump');
|
||||
Plugins.load('keyboard_shortcuts');
|
||||
Plugins.load('magic_key');
|
||||
Plugins.load('frequency_far_jump');
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Keyboard Shortcuts"
|
||||
permalink: /receiver/keyboard_shortcuts
|
||||
---
|
||||
|
||||
This `receiver` plugin will add keyboard shortcuts to your OWRX+.
|
||||
The plugin depends on [notify](https://0xaf.github.io/openwebrxplus-plugins/receiver/notify) plugin.
|
||||
|
||||
**OWRX+ v1.2.67 has this plugin integrated, hence the plugin will not install, even if loaded.**
|
||||
|
||||
## preview
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
To show help screen, press `?`.
|
||||
|
||||
## Load
|
||||
|
||||
Add this line in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/notify/notify.js');
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/keyboard_shortcuts/keyboard_shortcuts.js');
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Plugin: Keyboard Shortcuts
|
||||
*
|
||||
* Add keyboard shortcuts to OWRX interface.
|
||||
* press '?' to see help.
|
||||
*
|
||||
*/
|
||||
|
||||
/* import keyboard-css for keys in help */
|
||||
@import url("https://unpkg.com/keyboard-css@1.2.4/dist/css/main.min.css");
|
||||
|
||||
#ks-overlay {
|
||||
position: fixed;
|
||||
top: 30vh;
|
||||
left: calc((100vw - 800px) / 2);
|
||||
max-height: 60vh;
|
||||
width: 800px;
|
||||
color: white;
|
||||
background-color: #000;
|
||||
opacity: 0.8;
|
||||
z-index: 10000;
|
||||
border: 3px solid white;
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.ks-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.ks-subtitle {
|
||||
font-size: 0.8rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.ks-separator {
|
||||
border-top: 1px dotted white;
|
||||
align-self: stretch;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.ks-content {
|
||||
padding: 0 0.75rem;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.ks-item {
|
||||
width: 240px;
|
||||
padding: 0.25rem;
|
||||
border: 1px dashed #444;
|
||||
margin-bottom: 0.25rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
/* flex-direction: row; */
|
||||
}
|
||||
|
||||
.ks-item-kbd {
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
a.kbc-button-xs,
|
||||
button.kbc-button-xs {
|
||||
padding: 0.02rem 0.25rem;
|
||||
}
|
||||
|
||||
a.kbc-button-sm,
|
||||
button.kbc-button-sm {
|
||||
padding: 0 0.5rem;
|
||||
font-size: .85rem;
|
||||
}
|
||||
|
||||
/* we are using keyboard-css now, so no need of our key styling
|
||||
.ks-item kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 12px monospace;
|
||||
font-weight: bold;
|
||||
line-height: normal;
|
||||
vertical-align: middle;
|
||||
color: white;
|
||||
background-color: #555;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 6px;
|
||||
box-shadow: inset -0px -2px 0px 0px #aaa;
|
||||
} */
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* Plugin: Keyboard Shortcuts
|
||||
*
|
||||
* Add keyboard shortcuts to OWRX receiver interface.
|
||||
* press '?' for help.
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2023 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*/
|
||||
|
||||
// Plugin version
|
||||
Plugins.keyboard_shortcuts._version = 0.3;
|
||||
|
||||
// Initialize the plugin
|
||||
Plugins.keyboard_shortcuts.init = async function () {
|
||||
|
||||
if (window['Shortcuts'] && typeof (window.Shortcuts) === 'function') {
|
||||
console.error('This OWRX+ installation already have Keyboard Shortcuts. This plugin will not load.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Plugins.isLoaded('notify', 0.1)) {
|
||||
// try to load the notify plugin
|
||||
await Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/notify/notify.js');
|
||||
|
||||
// check again if it was loaded successfully
|
||||
if (!Plugins.isLoaded('notify', 0.1)) {
|
||||
console.error('Keyboard shortcuts plugin depends on "notify >= 0.1".');
|
||||
return false;
|
||||
} else {
|
||||
Plugins._debug('Plugin "notify" has been loaded as dependency.');
|
||||
}
|
||||
}
|
||||
|
||||
// create the help overlay
|
||||
Plugins.keyboard_shortcuts.overlay = jQuery('<div id="ks-overlay"></div>');
|
||||
Plugins.keyboard_shortcuts.overlay.hide();
|
||||
Plugins.keyboard_shortcuts.overlay.appendTo(document.body);
|
||||
|
||||
// catch all key presses
|
||||
$(document).on('keydown', function (e) {
|
||||
// check if we are focusing an input
|
||||
var on_input = !!($('input:focus').length && ($('input:focus')[0].type === 'text' || $('input:focus')[0].type === 'number'));
|
||||
// var on_input = !!($(':focus').is('input:text'));
|
||||
|
||||
// handle the global shortcuts, which will work even if an input is focused
|
||||
// please use modifier keys (like ctrl and alt) always
|
||||
var handled = false;
|
||||
switch (String(e.key).toLowerCase()) {
|
||||
// open chat
|
||||
case 'c':
|
||||
if (e.metaKey) {
|
||||
$('.button[data-toggle-panel="openwebrx-panel-log"').click();
|
||||
setTimeout(function () {
|
||||
$('#openwebrx-chat-message').focus();
|
||||
}, 100);
|
||||
handled = true;
|
||||
Plugins.notify.show('CHAT: toggle');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
break;
|
||||
|
||||
// open map
|
||||
case 'm':
|
||||
if (e.metaKey) {
|
||||
var z = $('a.button[href="map"]');
|
||||
$('a.button[target="openwebrx-map"]')[0].click();
|
||||
handled = true;
|
||||
Plugins.notify.show('MAP: open');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
break;
|
||||
// toggle panes
|
||||
case ' ':
|
||||
if (e.metaKey) {
|
||||
handled = true;
|
||||
if ($('#openwebrx-panel-receiver').is(':hidden')) {
|
||||
toggle_panel('openwebrx-panel-receiver', true);
|
||||
toggle_panel('openwebrx-panel-status', true);
|
||||
// toggle_panel('openwebrx-panel-log', true);
|
||||
} else {
|
||||
toggle_panel('openwebrx-panel-receiver', false);
|
||||
toggle_panel('openwebrx-panel-status', false);
|
||||
toggle_panel('openwebrx-panel-log', false);
|
||||
}
|
||||
Plugins.notify.show('Toggle panels');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// these shortcuts will be handled only when no input is focused
|
||||
if (!on_input && !handled)
|
||||
switch (String(e.key).toLowerCase()) {
|
||||
// hide help
|
||||
case 'escape':
|
||||
Plugins.keyboard_shortcuts.overlay.slideUp(100);
|
||||
break;
|
||||
|
||||
// show/hide help
|
||||
case '?':
|
||||
Plugins.keyboard_shortcuts.overlay.slideToggle(100);
|
||||
Plugins.notify.show('HELP: toggle');
|
||||
break;
|
||||
|
||||
// change to previous profile
|
||||
case ',':
|
||||
var sel = $('#openwebrx-sdr-profiles-listbox');
|
||||
var prev_val = sel.find(':selected').prev().val();
|
||||
if (prev_val) sel.val(prev_val).change();
|
||||
Plugins.notify.show('PROFILE: -');
|
||||
break;
|
||||
|
||||
// change to next profile
|
||||
case '.':
|
||||
var sel = $('#openwebrx-sdr-profiles-listbox');
|
||||
var next_val = sel.find(':selected').next().val();
|
||||
if (next_val) sel.val(next_val).change();
|
||||
Plugins.notify.show('PROFILE: +');
|
||||
break;
|
||||
|
||||
// change 10 profiles behind
|
||||
case '<':
|
||||
var sel = $('#openwebrx-sdr-profiles-listbox');
|
||||
var prev_el = sel.find(':selected'); // get current (option) element
|
||||
var last_val;
|
||||
for (var i = 0;
|
||||
(i < 10 && prev_el.val()); i++) {
|
||||
prev_el = prev_el.prev();
|
||||
if (prev_el && prev_el.val()) last_val = prev_el.val();
|
||||
}
|
||||
if (last_val) sel.val(last_val).change();
|
||||
Plugins.notify.show('PROFILE: -10');
|
||||
break;
|
||||
|
||||
// change 10 profile ahead
|
||||
case '>':
|
||||
var sel = $('#openwebrx-sdr-profiles-listbox');
|
||||
var next_el = sel.find(':selected'); // get current (option) element
|
||||
var last_val;
|
||||
for (var i = 0;
|
||||
(i < 10 && next_el.val()); i++) {
|
||||
next_el = next_el.next();
|
||||
if (next_el && next_el.val()) last_val = next_el.val();
|
||||
}
|
||||
if (last_val) sel.val(last_val).change();
|
||||
Plugins.notify.show('PROFILE: +10');
|
||||
break;
|
||||
|
||||
// open frequency input
|
||||
case 'f':
|
||||
$('.webrx-actual-freq').frequencyDisplay().inputGroup.click().focus();
|
||||
break;
|
||||
|
||||
// toggle mute
|
||||
case 'm':
|
||||
UI.toggleMute();
|
||||
Plugins.notify.show('MUTE: toggle');
|
||||
break;
|
||||
|
||||
// change volume
|
||||
case '-':
|
||||
case '+':
|
||||
case '=':
|
||||
var vol = $('#openwebrx-panel-volume');
|
||||
vol.val(parseInt(vol.val()) + (e.key === '-' ? -1 : +1)).change();
|
||||
Plugins.notify.show('VOL: ' + (e.key === '-' ? '-' : '+'));
|
||||
break;
|
||||
|
||||
// change squelch
|
||||
case ';':
|
||||
case '\'':
|
||||
var sql = $('.openwebrx-squelch-slider');
|
||||
sql.val(parseInt(sql.val()) + (e.key === ';' ? -1 : +1)).change();
|
||||
Plugins.notify.show('SQL: ' + (e.key === ';' ? '-' : '+'));
|
||||
break;
|
||||
|
||||
// auto set squelch / start scanner
|
||||
case 's':
|
||||
$('.openwebrx-squelch-auto').trigger(e.shiftKey ? 'contextmenu' : 'click');
|
||||
Plugins.notify.show((e.shiftKey ? 'SCANNER: toggle' : 'SQL: auto'));
|
||||
break;
|
||||
|
||||
// update waterfall colors
|
||||
case 'w':
|
||||
$('#openwebrx-waterfall-colors-auto').trigger(e.shiftKey ? 'contextmenu' : 'click');
|
||||
Plugins.notify.show((e.shiftKey ? 'WATERFALL: continuous' : 'WATERFALL: auto'));
|
||||
break;
|
||||
|
||||
// zoom controls
|
||||
case 'z':
|
||||
e.shiftKey ? zoomInTotal() : zoomInOneStep();
|
||||
Plugins.notify.show((e.shiftKey ? 'ZOOM: ++' : 'ZOOM: +'));
|
||||
break;
|
||||
case 'x':
|
||||
e.shiftKey ? zoomOutTotal() : zoomOutOneStep();
|
||||
Plugins.notify.show((e.shiftKey ? 'ZOOM: --' : 'ZOOM: -'));
|
||||
break;
|
||||
|
||||
// bookmarks
|
||||
case '{':
|
||||
case '}':
|
||||
var bms = $('#openwebrx-bookmarks-container .bookmark').find('.bookmark-content');
|
||||
var idx = Plugins.keyboard_shortcuts.bookmarkIdx;
|
||||
idx = typeof idx !== 'undefined' || idx == -1 ? idx : (e.key === '{' ? bms.length : -1);
|
||||
if (bms.length) {
|
||||
idx += (e.key === '{') ? -1 : 1; // change index
|
||||
idx = Math.min(Math.max(idx, 0), bms.length - 1); // limit to min/max
|
||||
bms.eq(idx).click();
|
||||
Plugins.keyboard_shortcuts.bookmarkIdx = parseInt(idx);
|
||||
Plugins.notify.show('BOOKMARK[' + (parseInt(idx) + 1) + ']: ' + bms.eq(idx).text());
|
||||
}
|
||||
break;
|
||||
|
||||
// enter change freq by step
|
||||
case '[':
|
||||
case ']':
|
||||
tuneBySteps(e.key === '[' ? -1 : 1);
|
||||
Plugins.notify.show('TUNE: ' + (e.key === '[' ? '-' : '+'));
|
||||
break;
|
||||
|
||||
// add bookmark
|
||||
case 'b':
|
||||
$('.openwebrx-bookmark-button').trigger('click');
|
||||
Plugins.notify.show('Add bookmark');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function gen_key(key) {
|
||||
var keymap = {
|
||||
',': ', <b style="font-size: 0.7rem">comma</b>',
|
||||
'.': '. <b style="font-size: 0.7rem">dot</b>',
|
||||
';': '; <b style="font-size: 0.7rem">semicolon</b>',
|
||||
'\'': '\' <b style="font-size: 0.7rem">apostrophe</b>',
|
||||
'SHIFT': '⇧ Shift',
|
||||
'CONTROL': '⌃ Ctrl',
|
||||
'COMMAND': '⌘ Cmd',
|
||||
'META': '⌘ Meta',
|
||||
'ALT': '⌥ Alt',
|
||||
'OPTION': '⌥ Opt',
|
||||
'ENTER': '↵ Enter',
|
||||
'RETURN': '↵ Enter',
|
||||
'DELETE': '⌦ Del',
|
||||
'BACKSPACE': '⌫ BS',
|
||||
'ESCAPE': '⎋ ESC',
|
||||
'ARROWRIGHT': '→',
|
||||
'ARROWLEFT': '←',
|
||||
'ARROWUP': '↑',
|
||||
'ARROWDOWN': '↓',
|
||||
'PAGEUP': '⇞ PgUp',
|
||||
'PAGEDOWN': '⇟ PgDn',
|
||||
'HOME': '↖ Home',
|
||||
'END': '↘ End',
|
||||
'TAB': '⇥ Tab',
|
||||
'SPACE': '␣ Space',
|
||||
'INTERVAL': '␣ Space',
|
||||
};
|
||||
var k = keymap[key.toUpperCase()] || key.toUpperCase();
|
||||
return `<button class="kbc-button kbc-button-sm" title="${key}"><b>${k}</b></button>`;
|
||||
}
|
||||
// fill the help overlay
|
||||
// i'm not using overlay.html('') on purpose
|
||||
// vscode syntax highlighting with 'nicolasparada.innerhtml' extension is working this way
|
||||
Plugins.keyboard_shortcuts.overlay[0].innerHTML = `
|
||||
<div class="ks-title">Keyboard shortcuts</div>
|
||||
<div class="ks-subtitle">Hide this help with '?' or Escape.</div>
|
||||
<div class="ks-separator"></div>
|
||||
<div class="ks-content">
|
||||
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key(',')}|${gen_key('.')}</div>
|
||||
<div class="ks-item-txt">change profile</div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('Z')}|${gen_key('X')}</div>
|
||||
<div class="ks-item-txt">zoom IN/OUT 1 step </div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('W')}</div>
|
||||
<div class="ks-item-txt">auto set waterfall colors</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('<')}|${gen_key('>')}</div>
|
||||
<div class="ks-item-txt">change profile by 10</div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('Shift')}+${gen_key('Z')}|${gen_key('X')}</div>
|
||||
<div class="ks-item-txt">zoom IN/OUT full</div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('Shift')}+${gen_key('W')}</div>
|
||||
<div class="ks-item-txt">continuous set waterfall colors</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('M')}|${gen_key('-')}|${gen_key('+/=')}</div>
|
||||
<div class="ks-item-txt">toggle mute or change volume</div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('S')}|${gen_key(';')}|${gen_key('\'')}</div>
|
||||
<div class="ks-item-txt">auto set or change squelch</div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('Shift')}+${gen_key('S')}</div>
|
||||
<div class="ks-item-txt">toggle scanner</div>
|
||||
</div>
|
||||
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('Meta')}+${gen_key('C')}</div>
|
||||
<div class="ks-item-txt">toggle chat panel</div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('Meta')}+${gen_key('M')}</div>
|
||||
<div class="ks-item-txt">open MAP</div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('Meta')}+${gen_key('Space')}</div>
|
||||
<div class="ks-item-txt">toggle all panel</div>
|
||||
</div>
|
||||
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('{')}|${gen_key('}')}</div>
|
||||
<div class="ks-item-txt">change bookmark</div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('[')}|${gen_key(']')}</div>
|
||||
<div class="ks-item-txt">tune freq by step</div>
|
||||
</div>
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('B')}</div>
|
||||
<div class="ks-item-txt">add local bookmark</div>
|
||||
</div>
|
||||
|
||||
<div class="ks-item">
|
||||
<div class="ks-item-kbd">${gen_key('F')}</div>
|
||||
<div class="ks-item-txt">freq input</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
// reset bookmark index on profile change.
|
||||
// this will work only if 'utils' plugin is loaded, but it's not a requirement
|
||||
$(document).on('event:profile_changed', function (e, data) {
|
||||
Plugins.keyboard_shortcuts.bookmarkIdx = -1;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
After Width: | Height: | Size: 569 KiB |
23
docker/openwebrx/plugins/receiver/magic_key/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: MagicKey"
|
||||
permalink: /receiver/magic_key
|
||||
---
|
||||
|
||||
This `receiver` plugin will allow you to set the MagicKey without typing it in the browser's address bar.
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||
|
||||
## Load
|
||||
|
||||
Add this line in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/magic_key/magic_key.js');
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
63
docker/openwebrx/plugins/receiver/magic_key/magic_key.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Plugin: MagicKey - Set the MagicKey without typing it in browser's address bar
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2024 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*/
|
||||
|
||||
|
||||
// no css for this plugin
|
||||
Plugins.magic_key.no_css = true;
|
||||
|
||||
// Initialize the plugin
|
||||
Plugins.magic_key.init = async function () {
|
||||
// Check if utils plugin is loaded
|
||||
if (!Plugins.isLoaded('utils', 0.3)) {
|
||||
console.error('Example plugin depends on "utils >= 0.3".');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($("#setMagicKeyBtn").length < 1) {
|
||||
$(".frequencies-container").after(`
|
||||
<div id="magic-key-line" class="openwebrx-panel-line openwebrx-panel-flex-line" style="display: none">
|
||||
<input id="magic-key-input" type="text" placeholder="Magic Key">
|
||||
<div id="magic-key-set" class="openwebrx-button">SET</div>
|
||||
</div>
|
||||
`);
|
||||
$(".openwebrx-bookmark-button").before(`
|
||||
<div id="setMagicKeyBtn" class="openwebrx-button openwebrx-square-button" style="width: 27px; height: 27px; text-align: center;">
|
||||
<svg fill="#FFFFFF" width="27px" height="27px" viewBox="-18.91 0 122.88 122.88" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"><g>
|
||||
<path d="M60.78,43.44c-1.49,0.81-3.35,0.26-4.15-1.22c-0.81-1.49-0.26-3.35,1.23-4.15c7.04-3.82,10.32-8.76,10.98-13.59 c0.35-2.58-0.05-5.17-1.02-7.57c-0.99-2.43-2.56-4.64-4.55-6.42c-3.87-3.46-9.3-5.28-14.97-3.87c-2.3,0.57-4.29,1.72-6.03,3.34 c-1.85,1.72-3.45,3.97-4.85,6.63c-0.79,1.5-2.64,2.07-4.13,1.29c-1.5-0.79-2.07-2.64-1.29-4.13c1.72-3.26,3.73-6.06,6.11-8.28 c2.49-2.31,5.38-3.97,8.74-4.8c7.8-1.93,15.23,0.53,20.51,5.25c2.68,2.4,4.81,5.39,6.15,8.69c1.35,3.33,1.9,6.99,1.39,10.7 C73.99,31.93,69.75,38.57,60.78,43.44L60.78,43.44z M37.32,67.61c-11.6-15.58-11.88-30.34,2.2-44.06l-10.14-5.6 C21.26,14.79,6.36,38.08,12.12,44.3l7.9,11.72l-1.63,3.4c-0.45,1.01-0.01,1.72,1.09,2.21l1.07,0.29L0,102.59l4.16,8.87l8.32-2.45 l2.14-4.16l-2.05-3.84l4.52-0.97L18.14,98l-2.36-3.6l1.55-3.01l4.51-0.57l1.47-2.85l-2.52-3.29l1.61-3.12l4.6-0.75l6.26-11.95 l1.06,0.58C36.16,70.56,37.11,69.84,37.32,67.61L37.32,67.61z M59.15,77.38l-3.06,11.42l-4.25,1.68l-0.89,3.33l3.1,2.63l-0.81,3.03 l-4.2,1.48l-0.86,3.2l3.01,2.95l-0.58,2.17l-4.13,1.87l2.76,3.25l-1.19,4.43l-7.45,4.07l-5.82-7.63l11.1-41.43l-2.69-0.72 c-0.55-0.15-0.89-0.72-0.74-1.28l1.13-4.21c-8.14-6.17-12.17-16.85-9.37-27.32c3.6-13.45,17.18-21.57,30.64-18.55 c0.06,0.72,0.05,1.45-0.05,2.18c-0.25,1.82-1.04,3.69-2.5,5.5c-0.2,0.24-0.41,0.49-0.63,0.73c-4.3-0.28-8.33,2.5-9.49,6.82 c-0.5,1.86-0.39,3.74,0.2,5.43c0.14,0.6,0.37,1.18,0.67,1.75c0.71,1.3,1.75,2.29,2.97,2.92c0.8,0.53,1.7,0.93,2.67,1.2 c4.83,1.29,9.78-1.49,11.22-6.24c1.46-1.29,2.73-2.65,3.82-4.05c2.12-2.73,3.57-5.63,4.43-8.58c5.84,6.3,8.41,15.37,6.02,24.29c-2.8,10.47-11.65,17.71-21.77,18.98l-1.13,4.21c-0.15,0.55-0.72,0.89-1.28,0.74L59.15,77.38L59.15,77.38z"/>
|
||||
</g></svg>
|
||||
</div>
|
||||
`);
|
||||
$('#setMagicKeyBtn').click(function () {
|
||||
$('#magic-key-line').toggle(250, function() {
|
||||
if ($(this).is(":visible")) $('#magic-key-input').focus();
|
||||
});
|
||||
});
|
||||
function setKey() {
|
||||
window.localKey = $('#magic-key-input').val();
|
||||
if (!window.localKey.length) {
|
||||
window.localKey = undefined;
|
||||
} else {
|
||||
$('#openwebrx-panel-receiver').demodulatorPanel().setMagicKey(window.localKey);
|
||||
}
|
||||
$('#magic-key-input').val('');
|
||||
$('#magic-key-line').toggle(250);
|
||||
}
|
||||
$('#magic-key-set').click(setKey);
|
||||
$('#magic-key-input').on('keypress',function(e) { if(e.which == 13) setKey(); });
|
||||
|
||||
Plugins.utils.wrap_func(
|
||||
'getMagicKey',
|
||||
// beforeCB: return true to call the afterCB
|
||||
function (orig, thisArg, args) { return true; },
|
||||
// afterCB: return localKey or the original one
|
||||
function (res) { return window.localKey !== undefined ? window.localKey : res; },
|
||||
$('#openwebrx-panel-receiver').demodulatorPanel() // wrap getMagicKey in the demodulatorPanel
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
BIN
docker/openwebrx/plugins/receiver/magic_key/magic_key.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
25
docker/openwebrx/plugins/receiver/notify/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Notify"
|
||||
permalink: /receiver/notify
|
||||
---
|
||||
|
||||
This is `utility` plugin. It provides notifications for other plugins.
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
Plugins.notify.show('some notification');
|
||||
```
|
||||
|
||||
## Load
|
||||
|
||||
Add this line in your `init.js` file:
|
||||
|
||||
```js
|
||||
await Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/notify/notify.js');
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
21
docker/openwebrx/plugins/receiver/notify/notify.css
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Plugin: Notifications for other plugins
|
||||
*
|
||||
*/
|
||||
|
||||
#plugins-notification {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
width: 200px;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
left: calc((100vw / 2) - 100px);
|
||||
border: 2px solid white;
|
||||
background: #333;
|
||||
margin: 20px;
|
||||
display: none;
|
||||
opacity: 0.9;
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
font-family: monospace;
|
||||
}
|
||||
37
docker/openwebrx/plugins/receiver/notify/notify.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Plugin: Provides notifications for other plugins
|
||||
*
|
||||
* Usage:
|
||||
* Plugins.notify.send('some notification');
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2023 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*
|
||||
*/
|
||||
|
||||
// Notify plugin version
|
||||
Plugins.notify._version = 0.1;
|
||||
|
||||
// Initialize the plugin
|
||||
Plugins.notify.init = function () {
|
||||
Plugins.notify.show = function (text) {
|
||||
// create the message div if it's not there
|
||||
if ($("#plugins-notification").length < 1)
|
||||
$("body").append("<div id='plugins-notification'></div>");
|
||||
|
||||
// set the message text
|
||||
$("#plugins-notification").html(text);
|
||||
|
||||
// show the message
|
||||
$("#plugins-notification").fadeIn('fast');
|
||||
|
||||
// clear the timeout of previous message (if exists)
|
||||
clearTimeout(Plugins.notify.notify_timeout);
|
||||
|
||||
// timeout the current message
|
||||
Plugins.notify.notify_timeout = setTimeout('$("#plugins-notification").fadeOut("fast")', 1000);
|
||||
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
30
docker/openwebrx/plugins/receiver/screen_reader/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Screen Reader"
|
||||
permalink: /receiver/screen_reader
|
||||
---
|
||||
|
||||
This `receiver` plugin will:
|
||||
|
||||
* Provide spoken notifications to users with Assistive Technology
|
||||
* Add invisible div for screen readers
|
||||
* Catch events and write to the div, so the screen reader will speak the content.
|
||||
|
||||
The plugin depends on [notify](https://0xaf.github.io/openwebrxplus-plugins/receiver/utils) v0.2 plugin.
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||
|
||||
## Load
|
||||
|
||||
Add this line in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/screen_reader/screen_reader.js');
|
||||
Plugins.screen_reader.log_messages = true; // if you want to log the messages to the chat window.
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Plugin: Screen Reader - Provide spoken notifications to users with Assistive Technology
|
||||
*
|
||||
* - Add invisible div for screen readers
|
||||
* - Catch events and write to the div, so the screen reader will speak the content.
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2024 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*/
|
||||
|
||||
// no css for this plugin
|
||||
Plugins.screen_reader.no_css = true;
|
||||
|
||||
// Plugins.screen_reader.log_messages = true;
|
||||
|
||||
// Initialize the plugin
|
||||
Plugins.screen_reader.init = async function () {
|
||||
|
||||
// Check if utils plugin is loaded
|
||||
if (!Plugins.isLoaded('utils', 0.2)) {
|
||||
// try to load the utils plugin
|
||||
await Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/utils/utils.js');
|
||||
|
||||
// check again if it was loaded successfully
|
||||
if (!Plugins.isLoaded('utils', 0.2)) {
|
||||
console.error('screen_reader plugin depends on "utils >= 0.2".');
|
||||
return false;
|
||||
} else {
|
||||
Plugins._debug('Plugin "utils" has been loaded as dependency.');
|
||||
}
|
||||
}
|
||||
|
||||
// create new screen reader div if it's not there
|
||||
if ($("#screen-reader").length < 1)
|
||||
$("body").append(
|
||||
"<div id='screen-reader' style='position:absolute; left:-10000px;top:auto;width:1px;height:1px;overflow:hidden;' role='status'></div>"
|
||||
);
|
||||
|
||||
// create speaker function
|
||||
Plugins.screen_reader.speak = function (text) {
|
||||
if (document.owrx_initialized) {
|
||||
clearTimeout(this.timeout);
|
||||
$("#screen-reader").append(text+"<br/>\n");
|
||||
this.timeout = setTimeout(function () { $("#screen-reader").html('') }, 3000); // clear after 3 secs
|
||||
// console.log('DEBUG:', $('#screen-reader').text());
|
||||
if (Plugins.screen_reader.log_messages) divlog(text);
|
||||
}
|
||||
}
|
||||
|
||||
// $(document).on('event:profile_changed', function (e, data) {
|
||||
// Plugins.screen_reader.speak("Profile " + data);
|
||||
// });
|
||||
$(document).on('event:owrx_initialized', function (e, data) {
|
||||
|
||||
$(document).on('server:config:after', function (e, data) {
|
||||
if (data.profile_id && data.sdr_id) { // profile was changed
|
||||
var name = $('#openwebrx-sdr-profiles-listbox')
|
||||
.find('option[value="' + data.sdr_id + '|' + data.profile_id + '"]').text();
|
||||
if (name) Plugins.screen_reader.speak("Profile: " + name);
|
||||
}
|
||||
});
|
||||
|
||||
Plugins.utils.wrap_func(
|
||||
'setMode',
|
||||
function (orig, thisArg, args) { // beforeCB -> return true to call the original
|
||||
if (this.last_mode === args[0]) return true; // same mode, no need to announce
|
||||
Plugins.screen_reader.speak("Mode: " + args[0]);
|
||||
this.last_mode = args[0];
|
||||
return true;
|
||||
},
|
||||
function (res) { // afterCB
|
||||
},
|
||||
DemodulatorPanel.prototype
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
// $(document).on('server:clients:after', function (e, data) {
|
||||
// });
|
||||
|
||||
// $(window).bind('beforeunload', function () {
|
||||
// });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
After Width: | Height: | Size: 91 KiB |
23
docker/openwebrx/plugins/receiver/screenshot/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: ScreenShot"
|
||||
permalink: /receiver/screenshot
|
||||
---
|
||||
|
||||
This `receiver` plugin will allow you to take screenshot of your waterfall.
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||
|
||||
## Load
|
||||
|
||||
Add this line in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/screenshot/screenshot.js');
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
20
docker/openwebrx/plugins/receiver/screenshot/html2canvas.min.js
vendored
Normal file
66
docker/openwebrx/plugins/receiver/screenshot/screenshot.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Plugin: Screenshot - Take a screenshot of the waterfall
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2024 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*/
|
||||
|
||||
|
||||
// no css for this plugin
|
||||
Plugins.screenshot.no_css = true;
|
||||
|
||||
// Initialize the plugin
|
||||
Plugins.screenshot.init = async function () {
|
||||
|
||||
await Plugins._load_script('https://0xaf.github.io/openwebrxplus-plugins/receiver/screenshot/html2canvas.min.js').catch(function() {
|
||||
throw("Cannot load html2canvas script.");
|
||||
});
|
||||
|
||||
|
||||
if ($("#screenshot-btn").length < 1) {
|
||||
$(".openwebrx-record-button").wrap("<div class='openwebrx-button openwebrx-square-button' style='background: none; width:2rem;'></div>");
|
||||
$(".openwebrx-record-button").after("<div id='screenshot-btn' class='openwebrx-button openwebrx-square-button'>PIC</div>");
|
||||
$(".openwebrx-record-button, #screenshot-btn").css({ float: 'none', marginTop: 0, width: '2rem', textAlign: 'center', padding: '2px 8px' });
|
||||
$('#screenshot-btn').click(function () {
|
||||
|
||||
var freq = window.center_freq + $('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator().get_offset_frequency();
|
||||
freq = parseInt(freq / 1000);
|
||||
freq = parseFloat(freq / 1000);
|
||||
console.log(freq);
|
||||
|
||||
let cloned_width = document.querySelector('#webrx-canvas-container').style.width;
|
||||
let cloned_height = 1200;
|
||||
var iframe_canvas = document.createElement("canvas");
|
||||
iframe_canvas.setAttribute('width', cloned_width);
|
||||
iframe_canvas.setAttribute('height', cloned_height);
|
||||
document.body.appendChild(iframe_canvas);
|
||||
html2canvas(document.querySelector('.openwebrx-waterfall-container'), {
|
||||
canvas: iframe_canvas,
|
||||
width: cloned_width,
|
||||
height: cloned_height,
|
||||
windowWidth: cloned_width,
|
||||
windowHeight: cloned_height,
|
||||
onclone: (e) => {
|
||||
// console.log(e);
|
||||
return e;
|
||||
}
|
||||
}).then(function (canvas) {
|
||||
// console.log(canvas);
|
||||
var d = new Date();
|
||||
var a = document.createElement("a");
|
||||
a.setAttribute('download', `sdr-screenshot-${freq.toFixed(3)}MHz-${d.toISOString()}.png`);
|
||||
a.setAttribute('href', canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"));
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(function () { document.body.removeChild(a); document.body.removeChild(iframe_canvas) }, 0);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
docker/openwebrx/plugins/receiver/screenshot/screenshot.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
23
docker/openwebrx/plugins/receiver/sort_profiles/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Sort Profiles by NAME"
|
||||
permalink: /receiver/sort_profiles
|
||||
---
|
||||
|
||||
This `receiver` plugin will sort your profile list by *name* (__NOT__ by frequency)
|
||||
This plugin is more an example for devs, than useful to users.
|
||||
|
||||
The plugin depends on [utils](https://0xaf.github.io/openwebrxplus-plugins/receiver/utils) plugin.
|
||||
|
||||
## Load
|
||||
|
||||
Add this lines in your `init.js` file:
|
||||
|
||||
```js
|
||||
await Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/utils/utils.js');
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/connect_notify/connect_notify.js');
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Plugin: sort profiles by name.
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2023 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*/
|
||||
|
||||
// do not load CSS for this plugin
|
||||
Plugins.sort_profiles.no_css = true;
|
||||
|
||||
// Initialize the plugin
|
||||
Plugins.sort_profiles.init = async function () {
|
||||
|
||||
// Check if utils plugin is loaded
|
||||
if (!Plugins.isLoaded('utils', 0.1)) {
|
||||
// try to load the utils plugin
|
||||
await Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/utils/utils.js');
|
||||
|
||||
// check again if it was loaded successfully
|
||||
if (!Plugins.isLoaded('utils', 0.1)) {
|
||||
console.error('soft_profiles plugin depends on "utils >= 0.1".');
|
||||
return false;
|
||||
} else {
|
||||
Plugins._debug('Plugin "utils" has been loaded as dependency.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Catch the event, when server sends us the profiles.
|
||||
$(document).on('server:profiles:after', function (e, data) {
|
||||
var sel = $('#openwebrx-sdr-profiles-listbox');
|
||||
|
||||
// if the list is empty, return
|
||||
if (!sel[0] || !sel[0].length)
|
||||
return;
|
||||
|
||||
var selected = sel.val();
|
||||
var list = sel.find('option');
|
||||
|
||||
// sort the list of options, alphanumeric and ignoring the case
|
||||
list.sort(function (a, b) {
|
||||
return $(a).text()
|
||||
.localeCompare(
|
||||
$(b).text(), undefined, {
|
||||
numeric: true,
|
||||
sensitivity: 'base'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// now reset the list and fill it with the new sorted one
|
||||
sel.html('').append(list);
|
||||
|
||||
// set the selected profile from our cached value
|
||||
sel.val(selected);
|
||||
});
|
||||
|
||||
// return true to validate plugin load
|
||||
return true;
|
||||
}
|
||||
21
docker/openwebrx/plugins/receiver/tune_checkbox/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Tune Checkbox"
|
||||
permalink: /receiver/tune_checkbox
|
||||
---
|
||||
|
||||
This a one-line `receiver` plugin to make the 'Hold mouse wheel down to tune' setting enabled by default.
|
||||
This setting allows you to use the mouse scroll to zoom into the waterfall.
|
||||
By default, the state of this checkbox is stored in localStorage in your browser. If you often delete your browser's cache and localStorage contents, this plugin might be useful.
|
||||
|
||||
## Load
|
||||
|
||||
Add this lines in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/tune_checkbox/tune_checkbox.js');
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Plugin: Make the 'Hold mouse wheel down to tune' option checked by default.
|
||||
* Allows you to zoom into the waterfall with the mouse scroll.
|
||||
*
|
||||
* By default, the state of this checkbox is stored in localStorage in your browser.
|
||||
* If you often delete your browser's cache and localStorage contents, this plugin might be useful.
|
||||
*
|
||||
* License: Apache License 2.0
|
||||
* Copyright (c) 2024 Dimitar Milkov, LZ2DMV
|
||||
*/
|
||||
|
||||
Plugins.tune_checkbox.no_css = true;
|
||||
|
||||
$('#openwebrx-wheel-checkbox').prop('checked', true).change();
|
||||
22
docker/openwebrx/plugins/receiver/utils/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
layout: page
|
||||
title: "OpenWebRX+ Receiver Plugin: Utils (utility)"
|
||||
permalink: /receiver/utils
|
||||
---
|
||||
|
||||
This `utility` plugin will give a function wrapping method and will send some events.
|
||||
This plugin is a dependency for almost all plugins.
|
||||
|
||||
## Load
|
||||
|
||||
Add this lines in your `init.js` file:
|
||||
|
||||
```js
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/utils/utils.js').then(async function () {
|
||||
// load the rest of your plugins here
|
||||
});
|
||||
```
|
||||
|
||||
## init.js
|
||||
|
||||
Learn how to [load plugins](/openwebrxplus-plugins/#load-plugins).
|
||||
165
docker/openwebrx/plugins/receiver/utils/utils.js
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Utils plugin.
|
||||
*
|
||||
* This plugin provides a function wrapping method (read below)
|
||||
* and adds some events for the rest plugins.
|
||||
*
|
||||
* License: MIT
|
||||
* Copyright (c) 2023 Stanislav Lechev [0xAF], LZ2SLL
|
||||
*
|
||||
* Changes:
|
||||
* 0.1:
|
||||
* - initial release
|
||||
* 0.2:
|
||||
* - add document.owrx_initialized boolean var, once initialized
|
||||
* - add _DEBUG_ALL_EVENTS
|
||||
* 0.3:
|
||||
* - handle return value of AfterCallBack of the wrapper
|
||||
*/
|
||||
|
||||
// Disable CSS loading for this plugin
|
||||
Plugins.utils.no_css = true;
|
||||
|
||||
// Utils plugin version
|
||||
Plugins.utils._version = 0.3;
|
||||
|
||||
/**
|
||||
* Wrap an existing function with before and after callbacks.
|
||||
* @param {string} name The name of function to wrap with before and after callbacks.
|
||||
* @param {function(orig, thisArg, args):boolean} before_cb Callback before original. Return true to call the original.
|
||||
* @param {function(result, orig, thisArg, args):any} after_cb Callback after original, will receive the result of original
|
||||
* @param {object} obj [optional] Object to look for function into. Default is 'window'
|
||||
* @description
|
||||
* - Before Callback:
|
||||
* - Params:
|
||||
* - orig: Original function (in case you want to call it, you have to return false to prevent second calling)
|
||||
* - thisArg: local 'this' for the original function
|
||||
* - args: arguments passed to the original function
|
||||
* - Returns: Boolean. Return false to prevent execution of original function and the after callback.
|
||||
* - After Callback:
|
||||
* - Params:
|
||||
* - res: Result of the original function
|
||||
* - thisArg: local 'this' for the original function
|
||||
* - args: arguments passed to the original function
|
||||
* - Returns: Any. Return anything to the original caller. This can be used to replace the original value.
|
||||
*
|
||||
* @example
|
||||
* // Using before and after callbacks.
|
||||
* Plugins.utils.wrap_func('sdr_profile_changed',
|
||||
* function (orig, thisArg, args) { // before callback
|
||||
* console.log(orig.name);
|
||||
* if (something_bad)
|
||||
* console.log('This profile is disabled by proxy function');
|
||||
* return false; // return false to prevent the calling of the original function and the after_cb()
|
||||
* }
|
||||
* return true; // always return true, to call the original function
|
||||
* },
|
||||
* function (res, thisArg, args) { // after callback
|
||||
* console.log(res);
|
||||
* return res;
|
||||
* }
|
||||
* );
|
||||
*
|
||||
* @example
|
||||
* // Using only before callback and handle original.
|
||||
* Plugins.utils.wrap_func('sdr_profile_changed',
|
||||
* function (orig, thisArg, args) { // before callback
|
||||
* // if we need to call the original in the middle of our work
|
||||
* do_something_before_original();
|
||||
* var res = orig.apply(thisArg, args);
|
||||
* do_something_after_original(res);
|
||||
* return false; // to prevent calling the original and after_cb
|
||||
* },
|
||||
* function (res) { // after callback
|
||||
* // ignored
|
||||
* return res;
|
||||
* }
|
||||
* );
|
||||
*
|
||||
*/
|
||||
Plugins.utils.wrap_func = function (name, before_cb, after_cb, obj = window) {
|
||||
if (typeof (obj[name]) !== "function") {
|
||||
console.error("Cannot wrap non existing function: '" + obj + '.' + name + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
var fn_original = obj[name];
|
||||
var proxy = new Proxy(obj[name], {
|
||||
apply: function (target, thisArg, args) {
|
||||
if (before_cb(target, thisArg, args)) {
|
||||
var orgRet = fn_original.apply(thisArg, args);
|
||||
var ret = after_cb(orgRet, thisArg, args);
|
||||
return ret !== undefined ? ret : orgRet;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
obj[name] = proxy;
|
||||
}
|
||||
|
||||
// Init utils plugin
|
||||
Plugins.utils.init = function () {
|
||||
var send_events_for = {};
|
||||
|
||||
// function name to proxy.
|
||||
send_events_for['sdr_profile_changed'] = {
|
||||
// [optional] event name (prepended with 'event:'). Default is function name.
|
||||
name: 'profile_changed',
|
||||
// [optional] data to send with the event (should be function).
|
||||
data: function () {
|
||||
return $('#openwebrx-sdr-profiles-listbox').find(':selected').text()
|
||||
}
|
||||
};
|
||||
|
||||
send_events_for['on_ws_recv'] = {
|
||||
// if we use handler, it will replace the before_cb
|
||||
handler: function (orig, thisArg, args) {
|
||||
if (typeof (args[0].data) === 'string' && args[0].data.substr(0, 16) !== "CLIENT DE SERVER") {
|
||||
try {
|
||||
var json = JSON.parse(args[0].data);
|
||||
if (Plugins.utils._DEBUG_ALL_EVENTS && json.type !== 'smeter')
|
||||
console.debug("server:" + json.type + ":before", [json['value']]);
|
||||
$(document).trigger('server:' + json.type + ":before", [json['value']]);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// we handle original function here
|
||||
orig.apply(thisArg, args);
|
||||
|
||||
if (typeof (json) === 'object') {
|
||||
if (Plugins.utils._DEBUG_ALL_EVENTS && json.type !== 'smeter')
|
||||
console.debug("server:" + json.type + ":after", [json['value']]);
|
||||
$(document).trigger('server:' + json.type + ":after", [json['value']]);
|
||||
}
|
||||
|
||||
// do not call the after_cb
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
$.each(send_events_for, function (key, obj) {
|
||||
Plugins.utils.wrap_func(
|
||||
key,
|
||||
typeof (obj.handler) === 'function' ? obj.handler : function () {
|
||||
return true;
|
||||
},
|
||||
function (res) {
|
||||
var ev_data;
|
||||
var ev_name = key;
|
||||
if (typeof (obj.name) === 'string') ev_name = obj.name;
|
||||
if (typeof (obj.data) === 'function') ev_data = obj.data(res);
|
||||
if (Plugins.utils._DEBUG_ALL_EVENTS) console.debug("event:" + ev_name, ev_data);
|
||||
$(document).trigger("event:" + ev_name, [ev_data]);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
var interval = setInterval(function () {
|
||||
if (typeof (clock) === 'undefined') return;
|
||||
clearInterval(interval);
|
||||
$(document).trigger('event:owrx_initialized');
|
||||
document.owrx_initialized = true;
|
||||
}, 10);
|
||||
|
||||
return true;
|
||||
}
|
||||
18
docker/openwebrx/restarter/script.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Restarter script started..."
|
||||
|
||||
while true; do
|
||||
|
||||
while [ $(date +%H:%M) != "02:00" ]; do
|
||||
sleep 30;
|
||||
done
|
||||
# Container time is UTC!!
|
||||
# sleep 86400 # Sleep for 24 hours
|
||||
|
||||
echo "Restarting Container ..."
|
||||
docker restart openwebrx_docker
|
||||
|
||||
sleep 60;
|
||||
|
||||
done
|
||||