implement profile editing page
This commit is contained in:
		@@ -5,6 +5,7 @@ from owrx.source import SdrDeviceDescription, SdrDeviceDescriptionMissing
 | 
				
			|||||||
from owrx.config import Config
 | 
					from owrx.config import Config
 | 
				
			||||||
from urllib.parse import quote, unquote
 | 
					from urllib.parse import quote, unquote
 | 
				
			||||||
from owrx.sdr import SdrService
 | 
					from owrx.sdr import SdrService
 | 
				
			||||||
 | 
					from abc import ABCMeta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SdrDeviceListController(AuthorizationMixin, WebpageController):
 | 
					class SdrDeviceListController(AuthorizationMixin, WebpageController):
 | 
				
			||||||
@@ -52,14 +53,11 @@ class SdrDeviceListController(AuthorizationMixin, WebpageController):
 | 
				
			|||||||
        self.serve_template("settings/general.html", **self.template_variables())
 | 
					        self.serve_template("settings/general.html", **self.template_variables())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SdrDeviceController(SettingsFormController):
 | 
					class SdrFormController(SettingsFormController, metaclass=ABCMeta):
 | 
				
			||||||
    def __init__(self, handler, request, options):
 | 
					    def __init__(self, handler, request, options):
 | 
				
			||||||
        super().__init__(handler, request, options)
 | 
					        super().__init__(handler, request, options)
 | 
				
			||||||
        self.device_id, self.device = self._get_device()
 | 
					        self.device_id, self.device = self._get_device()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getData(self):
 | 
					 | 
				
			||||||
        return self.device
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def store(self):
 | 
					    def store(self):
 | 
				
			||||||
        # need to overwrite the existing key in the config since the layering won't capture the changes otherwise
 | 
					        # need to overwrite the existing key in the config since the layering won't capture the changes otherwise
 | 
				
			||||||
        config = Config.get()
 | 
					        config = Config.get()
 | 
				
			||||||
@@ -68,10 +66,21 @@ class SdrDeviceController(SettingsFormController):
 | 
				
			|||||||
        config["sdrs"] = sdrs
 | 
					        config["sdrs"] = sdrs
 | 
				
			||||||
        super().store()
 | 
					        super().store()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_device(self):
 | 
				
			||||||
 | 
					        device_id = unquote(self.request.matches.group(1))
 | 
				
			||||||
 | 
					        if device_id not in Config.get()["sdrs"]:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        return device_id, Config.get()["sdrs"][device_id]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SdrDeviceController(SdrFormController):
 | 
				
			||||||
 | 
					    def getData(self):
 | 
				
			||||||
 | 
					        return self.device
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getSections(self):
 | 
					    def getSections(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            description = SdrDeviceDescription.getByType(self.device["type"])
 | 
					            description = SdrDeviceDescription.getByType(self.device["type"])
 | 
				
			||||||
            return [description.getSection()]
 | 
					            return [description.getDeviceSection()]
 | 
				
			||||||
        except SdrDeviceDescriptionMissing:
 | 
					        except SdrDeviceDescriptionMissing:
 | 
				
			||||||
            # TODO provide a generic interface that allows to switch the type
 | 
					            # TODO provide a generic interface that allows to switch the type
 | 
				
			||||||
            return []
 | 
					            return []
 | 
				
			||||||
@@ -79,12 +88,6 @@ class SdrDeviceController(SettingsFormController):
 | 
				
			|||||||
    def getTitle(self):
 | 
					    def getTitle(self):
 | 
				
			||||||
        return self.device["name"]
 | 
					        return self.device["name"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_device(self):
 | 
					 | 
				
			||||||
        device_id = unquote(self.request.matches.group(1))
 | 
					 | 
				
			||||||
        if device_id not in Config.get()["sdrs"]:
 | 
					 | 
				
			||||||
            return None
 | 
					 | 
				
			||||||
        return device_id, Config.get()["sdrs"][device_id]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def header_variables(self):
 | 
					    def header_variables(self):
 | 
				
			||||||
        variables = super().header_variables()
 | 
					        variables = super().header_variables()
 | 
				
			||||||
        variables["assets_prefix"] = "../../"
 | 
					        variables["assets_prefix"] = "../../"
 | 
				
			||||||
@@ -123,3 +126,47 @@ class SdrDeviceController(SettingsFormController):
 | 
				
			|||||||
            self.send_response("device not found", code=404)
 | 
					            self.send_response("device not found", code=404)
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        self.serve_template("settings/general.html", **self.template_variables())
 | 
					        self.serve_template("settings/general.html", **self.template_variables())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SdrProfileController(SdrFormController):
 | 
				
			||||||
 | 
					    def __init__(self, handler, request, options):
 | 
				
			||||||
 | 
					        super().__init__(handler, request, options)
 | 
				
			||||||
 | 
					        self.profile_id, self.profile = self._get_profile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getData(self):
 | 
				
			||||||
 | 
					        return self.profile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_profile(self):
 | 
				
			||||||
 | 
					        profile_id = unquote(self.request.matches.group(2))
 | 
				
			||||||
 | 
					        if self.device_id not in Config.get()["sdrs"]:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        if profile_id not in Config.get()["sdrs"][self.device_id]["profiles"]:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					        return profile_id, Config.get()["sdrs"][self.device_id]["profiles"][profile_id]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getSections(self):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            description = SdrDeviceDescription.getByType(self.device["type"])
 | 
				
			||||||
 | 
					            return [description.getProfileSection()]
 | 
				
			||||||
 | 
					        except SdrDeviceDescriptionMissing:
 | 
				
			||||||
 | 
					            # TODO provide a generic interface that allows to switch the type
 | 
				
			||||||
 | 
					            return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getTitle(self):
 | 
				
			||||||
 | 
					        return self.profile["name"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def header_variables(self):
 | 
				
			||||||
 | 
					        variables = super().header_variables()
 | 
				
			||||||
 | 
					        variables["assets_prefix"] = "../../../"
 | 
				
			||||||
 | 
					        return variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def template_variables(self):
 | 
				
			||||||
 | 
					        variables = super().template_variables()
 | 
				
			||||||
 | 
					        variables["assets_prefix"] = "../../../"
 | 
				
			||||||
 | 
					        return variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def indexAction(self):
 | 
				
			||||||
 | 
					        if self.profile is None:
 | 
				
			||||||
 | 
					            self.send_response("profile not found", code=404)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        self.serve_template("settings/general.html", **self.template_variables())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -313,3 +313,9 @@ class DropdownInput(Input):
 | 
				
			|||||||
class DropdownEnum(Enum):
 | 
					class DropdownEnum(Enum):
 | 
				
			||||||
    def toOption(self):
 | 
					    def toOption(self):
 | 
				
			||||||
        return Option(self.name, str(self))
 | 
					        return Option(self.name, str(self))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ModesInput(DropdownInput):
 | 
				
			||||||
 | 
					    def __init__(self, id, label):
 | 
				
			||||||
 | 
					        options = [Option(m.modulation, m.name) for m in Modes.getAvailableModes()]
 | 
				
			||||||
 | 
					        super().__init__(id, label, options)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								owrx/http.py
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								owrx/http.py
									
									
									
									
									
								
							@@ -6,7 +6,7 @@ from owrx.controllers.api import ApiController
 | 
				
			|||||||
from owrx.controllers.metrics import MetricsController
 | 
					from owrx.controllers.metrics import MetricsController
 | 
				
			||||||
from owrx.controllers.settings import SettingsController
 | 
					from owrx.controllers.settings import SettingsController
 | 
				
			||||||
from owrx.controllers.settings.general import GeneralSettingsController
 | 
					from owrx.controllers.settings.general import GeneralSettingsController
 | 
				
			||||||
from owrx.controllers.settings.sdr import SdrDeviceListController, SdrDeviceController
 | 
					from owrx.controllers.settings.sdr import SdrDeviceListController, SdrDeviceController, SdrProfileController
 | 
				
			||||||
from owrx.controllers.settings.reporting import ReportingController
 | 
					from owrx.controllers.settings.reporting import ReportingController
 | 
				
			||||||
from owrx.controllers.settings.backgrounddecoding import BackgroundDecodingController
 | 
					from owrx.controllers.settings.backgrounddecoding import BackgroundDecodingController
 | 
				
			||||||
from owrx.controllers.settings.decoding import DecodingSettingsController
 | 
					from owrx.controllers.settings.decoding import DecodingSettingsController
 | 
				
			||||||
@@ -117,11 +117,22 @@ class Router(object):
 | 
				
			|||||||
            ),
 | 
					            ),
 | 
				
			||||||
            StaticRoute("/settings/sdr", SdrDeviceListController),
 | 
					            StaticRoute("/settings/sdr", SdrDeviceListController),
 | 
				
			||||||
            RegexRoute("^/settings/sdr/([^/]+)$", SdrDeviceController),
 | 
					            RegexRoute("^/settings/sdr/([^/]+)$", SdrDeviceController),
 | 
				
			||||||
            RegexRoute("^/settings/sdr/([^/]+)$", SdrDeviceController, method="POST", options={"action": "processFormData"}),
 | 
					            RegexRoute(
 | 
				
			||||||
 | 
					                "^/settings/sdr/([^/]+)$", SdrDeviceController, method="POST", options={"action": "processFormData"}
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            RegexRoute("^/settings/sdr/([^/]+)/([^/]+)$", SdrProfileController),
 | 
				
			||||||
 | 
					            RegexRoute(
 | 
				
			||||||
 | 
					                "^/settings/sdr/([^/]+)/([^/]+)$",
 | 
				
			||||||
 | 
					                SdrProfileController,
 | 
				
			||||||
 | 
					                method="POST",
 | 
				
			||||||
 | 
					                options={"action": "processFormData"},
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
            StaticRoute("/settings/bookmarks", BookmarksController),
 | 
					            StaticRoute("/settings/bookmarks", BookmarksController),
 | 
				
			||||||
            StaticRoute("/settings/bookmarks", BookmarksController, method="POST", options={"action": "new"}),
 | 
					            StaticRoute("/settings/bookmarks", BookmarksController, method="POST", options={"action": "new"}),
 | 
				
			||||||
            RegexRoute("^/settings/bookmarks/(.+)$", BookmarksController, method="POST", options={"action": "update"}),
 | 
					            RegexRoute("^/settings/bookmarks/(.+)$", BookmarksController, method="POST", options={"action": "update"}),
 | 
				
			||||||
            RegexRoute("^/settings/bookmarks/(.+)$", BookmarksController, method="DELETE", options={"action": "delete"}),
 | 
					            RegexRoute(
 | 
				
			||||||
 | 
					                "^/settings/bookmarks/(.+)$", BookmarksController, method="DELETE", options={"action": "delete"}
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
            StaticRoute("/settings/reporting", ReportingController),
 | 
					            StaticRoute("/settings/reporting", ReportingController),
 | 
				
			||||||
            StaticRoute(
 | 
					            StaticRoute(
 | 
				
			||||||
                "/settings/reporting", ReportingController, method="POST", options={"action": "processFormData"}
 | 
					                "/settings/reporting", ReportingController, method="POST", options={"action": "processFormData"}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ from abc import ABC, abstractmethod
 | 
				
			|||||||
from owrx.command import CommandMapper
 | 
					from owrx.command import CommandMapper
 | 
				
			||||||
from owrx.socket import getAvailablePort
 | 
					from owrx.socket import getAvailablePort
 | 
				
			||||||
from owrx.property import PropertyStack, PropertyLayer
 | 
					from owrx.property import PropertyStack, PropertyLayer
 | 
				
			||||||
from owrx.form import Input, TextInput, NumberInput, CheckboxInput
 | 
					from owrx.form import Input, TextInput, NumberInput, CheckboxInput, ModesInput
 | 
				
			||||||
from owrx.form.converter import OptionalConverter
 | 
					from owrx.form.converter import OptionalConverter
 | 
				
			||||||
from owrx.form.device import GainInput
 | 
					from owrx.form.device import GainInput
 | 
				
			||||||
from owrx.controllers.settings import Section
 | 
					from owrx.controllers.settings import Section
 | 
				
			||||||
@@ -492,6 +492,11 @@ class SdrDeviceDescription(object):
 | 
				
			|||||||
            NumberInput("waterfall_min_level", "Lowest waterfall level", append="dBFS"),
 | 
					            NumberInput("waterfall_min_level", "Lowest waterfall level", append="dBFS"),
 | 
				
			||||||
            NumberInput("waterfall_max_level", "Highest waterfall level", append="dBFS"),
 | 
					            NumberInput("waterfall_max_level", "Highest waterfall level", append="dBFS"),
 | 
				
			||||||
            # TODO `schedule`
 | 
					            # TODO `schedule`
 | 
				
			||||||
 | 
					            NumberInput("center_freq", "Center frequency", append="Hz"),
 | 
				
			||||||
 | 
					            NumberInput("samp_rate", "Sample rate", append="S/s"),
 | 
				
			||||||
 | 
					            NumberInput("start_freq", "Initial frequency", append="Hz"),
 | 
				
			||||||
 | 
					            ModesInput("start_mod", "Initial modulation"),
 | 
				
			||||||
 | 
					            NumberInput("initial_squelch_level", "Initial squelch level", append="dBFS"),
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getMandatoryKeys(self):
 | 
					    def getMandatoryKeys(self):
 | 
				
			||||||
@@ -500,5 +505,19 @@ class SdrDeviceDescription(object):
 | 
				
			|||||||
    def getOptionalKeys(self):
 | 
					    def getOptionalKeys(self):
 | 
				
			||||||
        return ["ppm", "always-on", "services", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"]
 | 
					        return ["ppm", "always-on", "services", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getSection(self):
 | 
					    def getProfileMandatoryKeys(self):
 | 
				
			||||||
 | 
					        return ["center_freq", "samp_rate", "start_freq", "start_mod"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProfileOptionalKeys(self):
 | 
				
			||||||
 | 
					        return ["initial_squelch_level", "rf_gain", "lfo_offset", "waterfall_min_level", "waterfall_max_level"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getDeviceSection(self):
 | 
				
			||||||
        return OptionalSection("Device settings", self.getInputs(), self.getMandatoryKeys(), self.getOptionalKeys())
 | 
					        return OptionalSection("Device settings", self.getInputs(), self.getMandatoryKeys(), self.getOptionalKeys())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProfileSection(self):
 | 
				
			||||||
 | 
					        return OptionalSection(
 | 
				
			||||||
 | 
					            "Profile settings",
 | 
				
			||||||
 | 
					            self.getInputs(),
 | 
				
			||||||
 | 
					            self.getProfileMandatoryKeys(),
 | 
				
			||||||
 | 
					            self.getProfileOptionalKeys(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,5 +34,8 @@ class AirspyDeviceDescription(SoapyConnectorDeviceDescription):
 | 
				
			|||||||
    def getOptionalKeys(self):
 | 
					    def getOptionalKeys(self):
 | 
				
			||||||
        return super().getOptionalKeys() + ["bias_tee", "bitpack"]
 | 
					        return super().getOptionalKeys() + ["bias_tee", "bitpack"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProfileOptionalKeys(self):
 | 
				
			||||||
 | 
					        return super().getProfileOptionalKeys() + ["bias_tee"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getGainStages(self):
 | 
					    def getGainStages(self):
 | 
				
			||||||
        return ["LNA", "MIX", "VGA"]
 | 
					        return ["LNA", "MIX", "VGA"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -92,3 +92,6 @@ class ConnectorDeviceDescription(SdrDeviceDescription):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def getOptionalKeys(self):
 | 
					    def getOptionalKeys(self):
 | 
				
			||||||
        return super().getOptionalKeys() + ["rtltcp_compat", "iqswap"]
 | 
					        return super().getOptionalKeys() + ["rtltcp_compat", "iqswap"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProfileOptionalKeys(self):
 | 
				
			||||||
 | 
					        return super().getProfileOptionalKeys() + ["iqswap"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,5 +21,8 @@ class HackrfDeviceDescription(SoapyConnectorDeviceDescription):
 | 
				
			|||||||
    def getOptionalKeys(self):
 | 
					    def getOptionalKeys(self):
 | 
				
			||||||
        return super().getOptionalKeys() + ["bias_tee"]
 | 
					        return super().getOptionalKeys() + ["bias_tee"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProfileOptionalKeys(self):
 | 
				
			||||||
 | 
					        return super().getProfileOptionalKeys() + ["bias_tee"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getGainStages(self):
 | 
					    def getGainStages(self):
 | 
				
			||||||
        return ["LNA", "AMP", "VGA"]
 | 
					        return ["LNA", "AMP", "VGA"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,3 +29,6 @@ class RtlSdrDeviceDescription(ConnectorDeviceDescription):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def getOptionalKeys(self):
 | 
					    def getOptionalKeys(self):
 | 
				
			||||||
        return super().getOptionalKeys() + ["device", "bias_tee", "direct_sampling"]
 | 
					        return super().getOptionalKeys() + ["device", "bias_tee", "direct_sampling"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProfileOptionalKeys(self):
 | 
				
			||||||
 | 
					        return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,3 +20,6 @@ class RtlSdrSoapyDeviceDescription(SoapyConnectorDeviceDescription):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def getOptionalKeys(self):
 | 
					    def getOptionalKeys(self):
 | 
				
			||||||
        return super().getOptionalKeys() + ["bias_tee", "direct_sampling"]
 | 
					        return super().getOptionalKeys() + ["bias_tee", "direct_sampling"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProfileOptionalKeys(self):
 | 
				
			||||||
 | 
					        return super().getProfileOptionalKeys() + ["bias_tee", "direct_sampling"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,3 +56,6 @@ class SdrplayDeviceDescription(SoapyConnectorDeviceDescription):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def getOptionalKeys(self):
 | 
					    def getOptionalKeys(self):
 | 
				
			||||||
        return super().getOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"]
 | 
					        return super().getOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProfileOptionalKeys(self):
 | 
				
			||||||
 | 
					        return super().getProfileOptionalKeys() + ["bias_tee", "rf_notch", "dab_notch", "if_mode"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -100,5 +100,8 @@ class SoapyConnectorDeviceDescription(ConnectorDeviceDescription):
 | 
				
			|||||||
    def getOptionalKeys(self):
 | 
					    def getOptionalKeys(self):
 | 
				
			||||||
        return super().getOptionalKeys() + ["device", "rf_gain", "antenna"]
 | 
					        return super().getOptionalKeys() + ["device", "rf_gain", "antenna"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProfileOptionalKeys(self):
 | 
				
			||||||
 | 
					        return super().getProfileOptionalKeys() + ["antenna"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getGainStages(self):
 | 
					    def getGainStages(self):
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user