Skip to content

Commit 6d41493

Browse files
authored
Merge pull request #27 from CoMPaTech/testing
Add suggestion from HA Core 148989
2 parents 9ef09b4 + fedcaef commit 6d41493

File tree

3 files changed

+57
-69
lines changed

3 files changed

+57
-69
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: 56 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+
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

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
@@ -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
227263
class 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
305353
class 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)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "airos"
7-
version = "0.1.4"
7+
version = "0.1.5"
88
license = "MIT"
99
description = "Ubiquity airOS module(s) for Python 3."
1010
readme = "README.md"

0 commit comments

Comments
 (0)