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+ logger .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
@@ -222,6 +252,12 @@ class Remote:
222252 airview : int
223253 service : ServiceTime
224254
255+ @classmethod
256+ def __pre_deserialize__ (cls , d : dict [str , Any ]) -> dict [str , Any ]:
257+ """Pre-deserialize hook for Wireless."""
258+ _check_and_log_unknown_enum_value (d , "mode" , WirelessMode , "Wireless" , "mode" )
259+ return d
260+
225261
226262@dataclass
227263class Station :
@@ -266,8 +302,8 @@ class Wireless:
266302 """Leaf definition."""
267303
268304 essid : str
269- mode : WirelessMode | str # Allow non-breaking expansion
270- ieeemode : IeeeMode | str # Allow non-breaking expansion
305+ mode : WirelessMode
306+ ieeemode : IeeeMode
271307 band : int
272308 compat_11n : int
273309 hide_essid : int
@@ -277,7 +313,7 @@ class Wireless:
277313 center1_freq : int
278314 dfs : int
279315 distance : int
280- security : Security | str # Allow non-breaking expansion
316+ security : Security
281317 noisef : int
282318 txpower : int
283319 aprepeater : bool
@@ -300,6 +336,18 @@ class Wireless:
300336 sta : list [Station ]
301337 sta_disconnected : list [Any ]
302338
339+ @classmethod
340+ def __pre_deserialize__ (cls , d : dict [str , Any ]) -> dict [str , Any ]:
341+ """Pre-deserialize hook for Wireless."""
342+ _check_and_log_unknown_enum_value (d , "mode" , WirelessMode , "Wireless" , "mode" )
343+ _check_and_log_unknown_enum_value (
344+ d , "ieeemode" , IeeeMode , "Wireless" , "ieeemode"
345+ )
346+ _check_and_log_unknown_enum_value (
347+ d , "security" , Security , "Wireless" , "security"
348+ )
349+ return d
350+
303351
304352@dataclass
305353class InterfaceStatus :
@@ -375,57 +423,7 @@ class AirOSData(DataClassDictMixin):
375423 portfw : bool
376424 wireless : Wireless
377425 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
426+ provmode : Any
427+ ntpclient : Any
384428 unms : UnmsStatus
385429 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