diff --git a/owrx/config/classic.py b/owrx/config/classic.py index ba0fcbb..cbad127 100644 --- a/owrx/config/classic.py +++ b/owrx/config/classic.py @@ -17,14 +17,19 @@ class ClassicConfig(PropertyReadOnly): except FileNotFoundError: pass + @staticmethod + def _toLayer(dictionary: dict): + layer = PropertyLayer() + for k, v in dictionary.items(): + if isinstance(v, dict): + layer[k] = ClassicConfig._toLayer(v) + else: + layer[k] = v + return layer + @staticmethod def _loadPythonFile(file): spec = importlib.util.spec_from_file_location("config_webrx", file) cfg = importlib.util.module_from_spec(spec) spec.loader.exec_module(cfg) - pm = PropertyLayer() - for name, value in cfg.__dict__.items(): - if name.startswith("__"): - continue - pm[name] = value - return pm + return ClassicConfig._toLayer({k: v for k, v in cfg.__dict__.items() if not k.startswith("__")}) diff --git a/owrx/config/defaults.py b/owrx/config/defaults.py index 20d6516..6ce3820 100644 --- a/owrx/config/defaults.py +++ b/owrx/config/defaults.py @@ -8,7 +8,7 @@ defaultConfig = PropertyLayer( receiver_location="Budapest, Hungary", receiver_asl=200, receiver_admin="example@example.com", - receiver_gps={"lat": 47.0, "lon": 19.0}, + receiver_gps=PropertyLayer(lat=47.0, lon=19.0), photo_title="Panorama of Budapest from Schönherz Zoltán Dormitory", photo_desc="", fft_fps=9, @@ -25,7 +25,7 @@ defaultConfig = PropertyLayer( waterfall_scheme="GoogleTurboWaterfall", waterfall_min_level=-88, waterfall_max_level=-20, - waterfall_auto_level_margin={"min": 3, "max": 10, "min_range": 50}, + waterfall_auto_level_margin=PropertyLayer(min=3, max=10, min_range=50), frequency_display_precision=4, squelch_auto_margin=10, nmux_memory=50, @@ -34,7 +34,7 @@ defaultConfig = PropertyLayer( decoding_queue_workers=2, decoding_queue_length=10, wsjt_decoding_depth=3, - wsjt_decoding_depths={"jt65": 1}, + wsjt_decoding_depths=PropertyLayer(jt65=1), fst4_enabled_intervals=[15, 30], fst4w_enabled_intervals=[120, 300], q65_enabled_combinations=["A30", "E120", "C60"], diff --git a/owrx/config/dynamic.py b/owrx/config/dynamic.py index 30deae8..ebb78d5 100644 --- a/owrx/config/dynamic.py +++ b/owrx/config/dynamic.py @@ -1,6 +1,7 @@ from owrx.config.core import CoreConfig from owrx.config.migration import Migrator from owrx.property import PropertyLayer +from owrx.jsons import Encoder import json @@ -10,11 +11,24 @@ class DynamicConfig(PropertyLayer): try: with open(DynamicConfig._getSettingsFile(), "r") as f: for k, v in json.load(f).items(): - self[k] = v + if isinstance(v, dict): + self[k] = DynamicConfig._toLayer(v) + else: + self[k] = v except FileNotFoundError: pass Migrator.migrate(self) + @staticmethod + def _toLayer(dictionary: dict): + layer = PropertyLayer() + for k, v in dictionary.items(): + if isinstance(v, dict): + layer[k] = DynamicConfig._toLayer(v) + else: + layer[k] = v + return layer + @staticmethod def _getSettingsFile(): coreConfig = CoreConfig() @@ -22,6 +36,6 @@ class DynamicConfig(PropertyLayer): def store(self): # don't write directly to file to avoid corruption on exceptions - jsonContent = json.dumps(self.__dict__(), indent=4) + jsonContent = json.dumps(self.__dict__(), indent=4, cls=Encoder) with open(DynamicConfig._getSettingsFile(), "w") as file: file.write(jsonContent) diff --git a/owrx/form/converter.py b/owrx/form/converter.py index 3616727..5f9d15b 100644 --- a/owrx/form/converter.py +++ b/owrx/form/converter.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +from owrx.jsons import Encoder import json @@ -68,7 +69,7 @@ class EnumConverter(Converter): class JsonConverter(Converter): def convert_to_form(self, value): - return json.dumps(value) + return json.dumps(value, cls=Encoder) def convert_from_form(self, value): return json.loads(value) diff --git a/owrx/jsons.py b/owrx/jsons.py new file mode 100644 index 0000000..4b2b977 --- /dev/null +++ b/owrx/jsons.py @@ -0,0 +1,9 @@ +from owrx.property import PropertyManager +import json + + +class Encoder(json.JSONEncoder): + def default(self, o): + if isinstance(o, PropertyManager): + return o.__dict__() + return super().default(o) diff --git a/owrx/property/__init__.py b/owrx/property/__init__.py index 563c841..857b4cb 100644 --- a/owrx/property/__init__.py +++ b/owrx/property/__init__.py @@ -53,6 +53,12 @@ class PropertyManager(ABC): def keys(self): pass + def items(self): + return self.__dict__().items() + + def __len__(self): + return self.__dict__().__len__() + def filter(self, *props): return PropertyFilter(self, *props) diff --git a/owrx/sdr.py b/owrx/sdr.py index c926a3d..c326301 100644 --- a/owrx/sdr.py +++ b/owrx/sdr.py @@ -19,12 +19,6 @@ class SdrService(object): pm = Config.get() featureDetector = FeatureDetector() - def loadIntoPropertyManager(dict: dict): - propertyManager = PropertyLayer() - for (name, value) in dict.items(): - propertyManager[name] = value - return propertyManager - def sdrTypeAvailable(value): try: if not featureDetector.is_available(value["type"]): @@ -43,11 +37,11 @@ class SdrService(object): # transform all dictionary items into PropertyManager object, filtering out unavailable ones SdrService.sdrProps = { - name: loadIntoPropertyManager(value) for (name, value) in pm["sdrs"].items() if sdrTypeAvailable(value) + name: value for (name, value) in pm["sdrs"].items() if sdrTypeAvailable(value) } logger.info( "SDR sources loaded. Available SDRs: {0}".format( - ", ".join(map(lambda x: x["name"], SdrService.sdrProps.values())) + ", ".join(x["name"] for x in SdrService.sdrProps.values()) ) ) diff --git a/owrx/source/__init__.py b/owrx/source/__init__.py index 04a7a33..e6ab05b 100644 --- a/owrx/source/__init__.py +++ b/owrx/source/__init__.py @@ -100,7 +100,7 @@ class SdrSource(ABC): props = PropertyStack() props.addLayer(1, self.props) for id, p in self.props["profiles"].items(): - props.replaceLayer(0, self._getProfilePropertyLayer(p)) + props.replaceLayer(0, p) if "center_freq" not in props: logger.warning('Profile "%s" does not specify a center_freq', id) continue @@ -114,15 +114,6 @@ class SdrSource(ABC): if start_freq < center_freq - srh or start_freq > center_freq + srh: logger.warning('start_freq for profile "%s" is out of range', id) - def _getProfilePropertyLayer(self, profile): - layer = PropertyLayer() - for (key, value) in profile.items(): - # skip the name, that would overwrite the source name. - if key == "name": - continue - layer[key] = value - return layer - def isAlwaysOn(self): return "always-on" in self.props and self.props["always-on"] @@ -164,8 +155,7 @@ class SdrSource(ABC): profile = profiles[profile_id] self.profile_id = profile_id - layer = self._getProfilePropertyLayer(profile) - self.props.replaceLayer(0, layer) + self.props.replaceLayer(0, profile) def getId(self): return self.id diff --git a/owrx/websocket.py b/owrx/websocket.py index 4908e72..65ea7c9 100644 --- a/owrx/websocket.py +++ b/owrx/websocket.py @@ -1,3 +1,4 @@ +from owrx.jsons import Encoder import base64 import hashlib import json @@ -106,7 +107,7 @@ class WebSocketConnection(object): # convenience if type(data) == dict: # allow_nan = False disallows NaN and Infinty to be encoded. Browser JSON will not parse them anyway. - data = json.dumps(data, allow_nan=False) + data = json.dumps(data, allow_nan=False, cls=Encoder) # string-type messages are sent as text frames if type(data) == str: