openwebrx/owrx/controllers/settings/general.py

253 lines
10 KiB
Python
Raw Normal View History

from owrx.controllers.settings import SettingsFormController
from owrx.form.section import Section
2021-02-11 19:31:44 +01:00
from owrx.config.core import CoreConfig
2021-04-29 15:17:21 +02:00
from owrx.form.input import (
CheckboxInput,
2020-03-29 20:14:34 +02:00
TextInput,
NumberInput,
FloatInput,
TextAreaInput,
DropdownInput,
Option,
)
2021-04-29 15:17:21 +02:00
from owrx.form.input.converter import WaterfallColorsConverter, IntConverter
from owrx.form.input.receiverid import ReceiverKeysInput
2021-04-29 15:17:21 +02:00
from owrx.form.input.gfx import AvatarInput, TopPhotoInput
from owrx.form.input.device import WaterfallLevelsInput, WaterfallAutoLevelsInput
from owrx.form.input.location import LocationInput
from owrx.waterfall import WaterfallOptions
from owrx.breadcrumb import Breadcrumb, BreadcrumbItem
from owrx.controllers.settings import SettingsBreadcrumb
import shutil
2021-02-10 21:29:46 +01:00
import os
2021-05-07 16:57:54 +02:00
import re
2021-02-10 22:24:43 +01:00
from glob import glob
2020-03-26 23:04:02 +01:00
import logging
2020-04-26 02:15:19 +02:00
logger = logging.getLogger(__name__)
2020-04-25 21:55:52 +02:00
2021-02-15 15:40:37 +01:00
class GeneralSettingsController(SettingsFormController):
def getTitle(self):
return "General Settings"
2020-03-26 21:52:34 +01:00
def get_breadcrumb(self) -> Breadcrumb:
return SettingsBreadcrumb().append(BreadcrumbItem("General Settings", "settings/general"))
2021-02-15 15:40:37 +01:00
def getSections(self):
return [
Section(
"Receiver information",
TextInput("receiver_name", "Receiver name"),
TextInput("receiver_location", "Receiver location"),
NumberInput(
"receiver_asl",
"Receiver elevation",
append="meters above mean sea level",
),
TextInput("receiver_admin", "Receiver admin"),
LocationInput("receiver_gps", "Receiver coordinates"),
TextInput("photo_title", "Photo title"),
TextAreaInput("photo_desc", "Photo description", infotext="HTML supported "),
2021-02-15 15:40:37 +01:00
),
Section(
"Receiver images",
AvatarInput(
"receiver_avatar",
"Receiver Avatar",
infotext="For performance reasons, images are cached. "
+ "It can take a few hours until they appear on the site.",
),
TopPhotoInput(
"receiver_top_photo",
"Receiver Panorama",
infotext="For performance reasons, images are cached. "
+ "It can take a few hours until they appear on the site.",
),
),
Section(
"Receiver limits",
NumberInput(
"max_clients",
"Maximum number of clients",
),
),
Section(
"Receiver listings",
ReceiverKeysInput(
2021-02-15 15:40:37 +01:00
"receiver_keys",
"Receiver keys",
),
),
Section(
"Waterfall settings",
DropdownInput(
"waterfall_scheme",
"Waterfall color scheme",
options=WaterfallOptions,
),
TextAreaInput(
"waterfall_colors",
"Custom waterfall colors",
2021-02-16 18:39:42 +01:00
infotext="Please provide 6-digit hexadecimal RGB colors in HTML notation (#RRGGBB)"
+ " or HEX notation (0xRRGGBB), one per line",
converter=WaterfallColorsConverter(),
),
2021-02-15 15:40:37 +01:00
NumberInput(
"fft_fps",
"FFT speed",
infotext="This setting specifies how many lines are being added to the waterfall per second. "
+ "Higher values will give you a faster waterfall, but will also use more CPU.",
append="frames per second",
),
NumberInput("fft_size", "FFT size", append="bins"),
FloatInput(
"fft_voverlap_factor",
"FFT vertical overlap factor",
infotext="If fft_voverlap_factor is above 0, multiple FFTs will be used for creating a line on the "
+ "diagram.",
),
WaterfallLevelsInput("waterfall_levels", "Waterfall levels"),
WaterfallAutoLevelsInput(
"waterfall_auto_levels",
"Automatic adjustment margins",
infotext="Specifies the upper and lower dynamic headroom that should be added when automatically "
+ "adjusting waterfall colors",
),
CheckboxInput(
"waterfall_auto_level_default_mode",
"Automatically adjust waterfall level by default",
infotext="Enable this to automatically enable auto adjusting waterfall levels on page load.",
),
NumberInput(
"waterfall_auto_min_range",
"Automatic adjustment minimum range",
append="dB",
infotext="Minimum dynamic range the waterfall should cover after automatically adjusting "
+ "waterfall colors",
),
2021-02-15 15:40:37 +01:00
),
Section(
"Compression",
DropdownInput(
"audio_compression",
"Audio compression",
options=[
Option("adpcm", "ADPCM"),
Option("none", "None"),
],
),
DropdownInput(
"fft_compression",
"Waterfall compression",
options=[
Option("adpcm", "ADPCM"),
Option("none", "None"),
],
),
),
Section(
"Display settings",
2021-03-24 23:43:19 +01:00
DropdownInput(
"tuning_precision",
"Tuning precision",
2021-03-24 23:43:19 +01:00
options=[Option(str(i), "{} Hz".format(10 ** i)) for i in range(0, 6)],
converter=IntConverter(),
2021-02-15 15:40:37 +01:00
),
),
Section(
"Map settings",
DropdownInput(
"map_type",
"Map type",
options=[
Option("google", "Google Maps"),
Option("leaflet", "OpenStreetMap, etc."),
],
),
2021-02-15 15:40:37 +01:00
TextInput(
"google_maps_api_key",
"Google Maps API key",
infotext="Google Maps requires an API key, check out "
+ '<a href="https://developers.google.com/maps/documentation/embed/get-api-key" target="_blank">'
+ "their documentation</a> on how to obtain one.",
),
NumberInput(
"map_position_retention_time",
"Map retention time",
2023-05-15 20:50:08 +02:00
infotext="Specifies for how long markers / grids will remain visible on the map",
2021-02-15 15:40:37 +01:00
append="s",
),
DropdownInput(
"callsign_service",
"Callsign database service",
infotext="Allows users to navigate to an external callsign database service by clicking on "
+ "callsigns",
options=[
Option(None, "disabled"),
Option("qrzcq", "qrzcq.com"),
Option("qrz", "qrz.com"),
Option("aprsfi", "aprs.fi"),
],
),
2023-08-28 21:30:31 +02:00
DropdownInput(
"aircraft_tracking_service",
"Aircraft tracking service",
infotext="Allows users to navigate to an external flight tracking service by clicking on flight "
+ "numbers",
options=[
Option(None, "disabled"),
Option("flightaware", "FlightAware"),
Option("planefinder", "planefinder"),
]
)
2021-02-15 15:40:37 +01:00
),
]
2020-03-26 23:04:02 +01:00
def remove_existing_image(self, image_id):
config = CoreConfig()
# remove all possible file extensions
for ext in ["png", "jpg", "webp"]:
try:
os.unlink("{}/{}.{}".format(config.get_data_directory(), image_id, ext))
except FileNotFoundError:
pass
def handle_image(self, data, image_id):
2021-02-10 21:29:46 +01:00
if image_id in data:
2021-02-09 00:38:59 +01:00
config = CoreConfig()
2021-02-10 21:29:46 +01:00
if data[image_id] == "restore":
self.remove_existing_image(image_id)
2021-02-10 21:29:46 +01:00
elif data[image_id]:
if not data[image_id].startswith(image_id):
logger.warning("invalid file name: %s", data[image_id])
else:
2021-05-07 16:57:54 +02:00
# get file extension (at least 3 characters)
# should be all lowercase since they are set by the upload script
pattern = re.compile(".*\\.([a-z]{3,})$")
matches = pattern.match(data[image_id])
if matches is None:
logger.warning("could not determine file extension for %s", image_id)
else:
self.remove_existing_image(image_id)
2021-05-07 16:57:54 +02:00
ext = matches.group(1)
data_file = "{}/{}.{}".format(config.get_data_directory(), image_id, ext)
temporary_file = "{}/{}".format(config.get_temporary_directory(), data[image_id])
shutil.copy(temporary_file, data_file)
2021-02-10 21:29:46 +01:00
del data[image_id]
2021-02-10 22:24:43 +01:00
# remove any accumulated temporary files on save
for file in glob("{}/{}*".format(config.get_temporary_directory(), image_id)):
os.unlink(file)
2021-02-15 15:40:37 +01:00
def processData(self, data):
# Image handling
2021-02-10 22:25:43 +01:00
for img in ["receiver_avatar", "receiver_top_photo"]:
self.handle_image(data, img)
# special handling for waterfall colors: custom colors only stay in config if custom color scheme is selected
if "waterfall_scheme" in data:
scheme = WaterfallOptions(data["waterfall_scheme"])
if scheme is not WaterfallOptions.CUSTOM and "waterfall_colors" in data:
data["waterfall_colors"] = None
2021-02-15 15:40:37 +01:00
super().processData(data)