11"""Provide mashumaro data object for AirOSData."""
22
3- from dataclasses import dataclass , field
3+ from dataclasses import dataclass
44from enum import Enum
5+ import logging
56from typing import Any
67
78from mashumaro import DataClassDictMixin
89
10+ logger = logging .getLogger (__name__ )
11+
12+
13+ def _check_and_log_unknown_enum_value (
14+ data_dict : dict [str , Any ],
15+ key : str ,
16+ enum_class : type [Enum ],
17+ dataclass_name : str ,
18+ field_name : str ,
19+ ) -> None :
20+ """Clean unsupported parameters with logging."""
21+ value = data_dict .get (key )
22+ if value is not None and isinstance (value , str ):
23+ if value not in [e .value for e in enum_class ]:
24+ logging .warning (
25+ "Unknown value '%s' for %s.%s. Please report at "
26+ "https://github.com/CoMPaTech/python-airos/issues so we can add support." ,
27+ value ,
28+ dataclass_name ,
29+ field_name ,
30+ )
31+ del data_dict [key ]
32+
933
1034class IeeeMode (Enum ):
1135 """Enum definition."""
@@ -58,14 +82,20 @@ class Host:
5882 timestamp : int
5983 fwversion : str
6084 devmodel : str
61- netrole : NetRole | str
85+ netrole : NetRole
6286 loadavg : float
6387 totalram : int
6488 freeram : int
6589 temperature : int
6690 cpuload : float
6791 height : int
6892
93+ @classmethod
94+ def __pre_deserialize__ (cls , d : dict [str , Any ]) -> dict [str , Any ]:
95+ """Pre-deserialize hook for Host."""
96+ _check_and_log_unknown_enum_value (d , "netrole" , NetRole , "Host" , "netrole" )
97+ return d
98+
6999
70100@dataclass
71101class Services :
@@ -193,7 +223,7 @@ class Remote:
193223 totalram : int
194224 freeram : int
195225 netrole : str
196- mode : WirelessMode | str # Allow non-breaking future expansion
226+ mode : WirelessMode
197227 sys_id : str
198228 tx_throughput : int
199229 rx_throughput : int
@@ -266,8 +296,8 @@ class Wireless:
266296 """Leaf definition."""
267297
268298 essid : str
269- mode : WirelessMode | str # Allow non-breaking expansion
270- ieeemode : IeeeMode | str # Allow non-breaking expansion
299+ mode : WirelessMode
300+ ieeemode : IeeeMode
271301 band : int
272302 compat_11n : int
273303 hide_essid : int
@@ -277,7 +307,7 @@ class Wireless:
277307 center1_freq : int
278308 dfs : int
279309 distance : int
280- security : Security | str # Allow non-breaking expansion
310+ security : Security
281311 noisef : int
282312 txpower : int
283313 aprepeater : bool
@@ -300,6 +330,18 @@ class Wireless:
300330 sta : list [Station ]
301331 sta_disconnected : list [Any ]
302332
333+ @classmethod
334+ def __pre_deserialize__ (cls , d : dict [str , Any ]) -> dict [str , Any ]:
335+ """Pre-deserialize hook for Wireless."""
336+ _check_and_log_unknown_enum_value (d , "mode" , WirelessMode , "Wireless" , "mode" )
337+ _check_and_log_unknown_enum_value (
338+ d , "ieeemode" , IeeeMode , "Wireless" , "ieeemode"
339+ )
340+ _check_and_log_unknown_enum_value (
341+ d , "security" , Security , "Wireless" , "security"
342+ )
343+ return d
344+
303345
304346@dataclass
305347class InterfaceStatus :
@@ -375,57 +417,7 @@ class AirOSData(DataClassDictMixin):
375417 portfw : bool
376418 wireless : Wireless
377419 interfaces : list [Interface ]
378- provmode : (
379- ProvisioningMode | str | dict [str , Any ] | list [Any ] | Any
380- ) # If it can be populated, define its fields
381- ntpclient : (
382- NtpClient | str | dict [str , Any ] | list [Any ] | Any
383- ) # If it can be populated, define its fields
420+ provmode : Any
421+ ntpclient : Any
384422 unms : UnmsStatus
385423 gps : GPSMain
386- warnings : dict [str , list [str ]] = field (default_factory = dict , init = False )
387-
388- @classmethod
389- def __post_deserialize__ (cls , airos_object : "AirOSData" ) -> "AirOSData" :
390- """Validate after deserialization."""
391- airos_object .check_for_warnings ()
392- return airos_object
393-
394- def check_for_warnings (self ):
395- """Validate unions for unknown fields."""
396- # Check wireless mode
397- if isinstance (self .wireless .mode , str ):
398- self .add_warning (
399- "wireless" , f"Unknown (new) wireless mode: '{ self .wireless .mode } '"
400- )
401-
402- # Check host netrole
403- if isinstance (self .host .netrole , str ):
404- self .add_warning (
405- "host" , f"Unknown (new) network role: '{ self .host .netrole } '"
406- )
407-
408- # Check wireless IEEE mode
409- if isinstance (self .wireless .ieeemode , str ):
410- self .add_warning (
411- "wireless" , f"Unknown (new) IEEE mode: '{ self .wireless .ieeemode } '"
412- )
413-
414- # Check wireless security
415- if isinstance (self .wireless .security , str ):
416- self .add_warning (
417- "wireless" , f"Unknown (new) security type: '{ self .wireless .security } '"
418- )
419- # Check station remote modes
420- for i , station in enumerate (self .wireless .sta ):
421- if hasattr (station .remote , "mode" ) and isinstance (station .remote .mode , str ):
422- self .add_warning (
423- f"wireless.sta[{ i } ].remote" ,
424- f"Unknown (new) remote mode: '{ station .remote .mode } ', please report to the CODEOWNERS for inclusion" ,
425- )
426-
427- def add_warning (self , field_name : str , message : str ):
428- """Insert warnings into the dictionary on unknown field data."""
429- if field_name not in self .warnings :
430- self .warnings [field_name ] = []
431- self .warnings [field_name ].append (message )
0 commit comments