Skip to content

Commit d030657

Browse files
committed
Add suggestion from HA Core 148989
1 parent 39e7737 commit d030657

File tree

2 files changed

+50
-68
lines changed

2 files changed

+50
-68
lines changed

airos/airos8.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ def __init__(
5252
self._status_cgi_url = f"{self.base_url}/status.cgi" # AirOS 8
5353
self._stakick_cgi_url = f"{self.base_url}/stakick.cgi" # AirOS 8
5454
self.current_csrf_token = None
55-
self.warnings_cache = []
5655

5756
self._use_json_for_login_post = False
5857

@@ -214,15 +213,6 @@ async def status(self) -> AirOSData:
214213
logger.exception("Failed to deserialize AirOS data")
215214
raise KeyDataMissingError from err
216215

217-
# Show new enums detected, once after (each) startup
218-
if airos_data.warnings:
219-
for field_name, messages in airos_data.warnings.items():
220-
for msg in messages:
221-
log = f"AirOS data warning for field '{field_name}': {msg}"
222-
if log not in self.warnings_cache:
223-
self.warnings_cache.append(log)
224-
logger.warning(log)
225-
226216
return airos_data
227217
except json.JSONDecodeError:
228218
logger.exception(

airos/airos8data.py

Lines changed: 50 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
11
"""Provide mashumaro data object for AirOSData."""
22

3-
from dataclasses import dataclass, field
3+
from dataclasses import dataclass
44
from enum import Enum
5+
import logging
56
from typing import Any
67

78
from 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

1034
class 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
71101
class 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
305347
class 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

Comments
 (0)