Skip to content

Commit a41436e

Browse files
authored
Merge pull request #68 from CoMPaTech/upd
Add warning and update-check endpoints, improve quality
2 parents aa8ce26 + b83197a commit a41436e

16 files changed

+264
-5
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.3.0] - 2025-08-15
6+
7+
### Added
8+
9+
- Implementation of `[AP|Sta]-[MODE]` to Enums.
10+
- Added update check (non-forced) endpoint
11+
- Added warnings fetch endpoint
12+
513
## [0.2.11] - 2025-08-14
614

715
### Changed

airos/airos8.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@
1111
import aiohttp
1212
from mashumaro.exceptions import InvalidFieldValue, MissingField
1313

14-
from .data import AirOS8Data as AirOSData, redact_data_smart
14+
from .data import (
15+
AirOS8Data as AirOSData,
16+
DerivedWirelessMode,
17+
DerivedWirelessRole,
18+
redact_data_smart,
19+
)
1520
from .exceptions import (
1621
AirOSConnectionAuthenticationError,
1722
AirOSConnectionSetupError,
@@ -54,6 +59,8 @@ def __init__(
5459
self._status_cgi_url = f"{self.base_url}/status.cgi" # AirOS 8
5560
self._stakick_cgi_url = f"{self.base_url}/stakick.cgi" # AirOS 8
5661
self._provmode_url = f"{self.base_url}/api/provmode" # AirOS 8
62+
self._warnings_url = f"{self.base_url}/api/warnings" # AirOS 8
63+
self._update_check_url = f"{self.base_url}/api/fw/update-check" # AirOS 8
5764
self.current_csrf_token: str | None = None
5865

5966
self._use_json_for_login_post = False
@@ -201,6 +208,8 @@ def derived_data(response: dict[str, Any]) -> dict[str, Any]:
201208
"access_point": False,
202209
"ptp": False,
203210
"ptmp": False,
211+
"role": DerivedWirelessRole.STATION,
212+
"mode": DerivedWirelessMode.PTP,
204213
}
205214

206215
# Access Point / Station vs PTP/PtMP
@@ -209,12 +218,16 @@ def derived_data(response: dict[str, Any]) -> dict[str, Any]:
209218
case "ap-ptmp":
210219
derived["access_point"] = True
211220
derived["ptmp"] = True
221+
derived["role"] = DerivedWirelessRole.ACCESS_POINT
222+
derived["mode"] = DerivedWirelessMode.PTMP
212223
case "sta-ptmp":
213224
derived["station"] = True
214225
derived["ptmp"] = True
226+
derived["mode"] = DerivedWirelessMode.PTMP
215227
case "ap-ptp":
216228
derived["access_point"] = True
217229
derived["ptp"] = True
230+
derived["role"] = DerivedWirelessRole.ACCESS_POINT
218231
case "sta-ptp":
219232
derived["station"] = True
220233
derived["ptp"] = True
@@ -384,3 +397,69 @@ async def provmode(self, active: bool = False) -> bool:
384397
except asyncio.CancelledError:
385398
_LOGGER.info("Provisioning mode change task was cancelled")
386399
raise
400+
401+
async def warnings(self) -> dict[str, Any] | Any:
402+
"""Get warnings."""
403+
if not self.connected:
404+
_LOGGER.error("Not connected, login first")
405+
raise AirOSDeviceConnectionError from None
406+
407+
request_headers = {**self._common_headers}
408+
if self.current_csrf_token:
409+
request_headers["X-CSRF-ID"] = self.current_csrf_token
410+
411+
# Formal call is '/api/warnings?_=1755249683586'
412+
try:
413+
async with self.session.get(
414+
self._warnings_url,
415+
headers=request_headers,
416+
) as response:
417+
response_text = await response.text()
418+
if response.status == 200:
419+
return json.loads(response_text)
420+
log = f"Unable to fech warning status {response.status} with {response_text}"
421+
_LOGGER.error(log)
422+
raise AirOSDataMissingError from None
423+
except json.JSONDecodeError:
424+
_LOGGER.exception("JSON Decode Error in warning response")
425+
raise AirOSDataMissingError from None
426+
except (TimeoutError, aiohttp.ClientError) as err:
427+
_LOGGER.exception("Error during call to retrieve warnings: %s", err)
428+
raise AirOSDeviceConnectionError from err
429+
except asyncio.CancelledError:
430+
_LOGGER.info("Warning check task was cancelled")
431+
raise
432+
433+
async def update_check(self) -> dict[str, Any] | Any:
434+
"""Get warnings."""
435+
if not self.connected:
436+
_LOGGER.error("Not connected, login first")
437+
raise AirOSDeviceConnectionError from None
438+
439+
request_headers = {**self._common_headers}
440+
if self.current_csrf_token:
441+
request_headers["X-CSRF-ID"] = self.current_csrf_token
442+
request_headers["Content-type"] = "application/json"
443+
444+
# Post without data
445+
try:
446+
async with self.session.post(
447+
self._update_check_url,
448+
headers=request_headers,
449+
json={},
450+
) as response:
451+
response_text = await response.text()
452+
if response.status == 200:
453+
return json.loads(response_text)
454+
log = f"Unable to fech update status {response.status} with {response_text}"
455+
_LOGGER.error(log)
456+
raise AirOSDataMissingError from None
457+
except json.JSONDecodeError:
458+
_LOGGER.exception("JSON Decode Error in warning response")
459+
raise AirOSDataMissingError from None
460+
except (TimeoutError, aiohttp.ClientError) as err:
461+
_LOGGER.exception("Error during call to retrieve update status: %s", err)
462+
raise AirOSDeviceConnectionError from err
463+
except asyncio.CancelledError:
464+
_LOGGER.info("Warning update status task was cancelled")
465+
raise

airos/data.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,20 @@ class IeeeMode(Enum):
141141
# More to be added when known
142142

143143

144+
class DerivedWirelessRole(Enum):
145+
"""Enum definition."""
146+
147+
STATION = "station"
148+
ACCESS_POINT = "access_point"
149+
150+
151+
class DerivedWirelessMode(Enum):
152+
"""Enum definition."""
153+
154+
PTP = "point_to_point"
155+
PTMP = "point_to_multipoint"
156+
157+
144158
class WirelessMode(Enum):
145159
"""Enum definition."""
146160

@@ -350,7 +364,7 @@ class Remote(AirOSDataClass):
350364
rssi: int
351365
noisefloor: int
352366
tx_power: int
353-
distance: int
367+
distance: int # In meters
354368
rx_chainmask: int
355369
chainrssi: list[int]
356370
tx_ratedata: list[int]
@@ -408,7 +422,7 @@ class Station(AirOSDataClass):
408422
tx_nss: int
409423
rx_nss: int
410424
tx_latency: int
411-
distance: int
425+
distance: int # In meters
412426
tx_packets: int
413427
tx_lretries: int
414428
tx_sretries: int
@@ -446,7 +460,7 @@ class Wireless(AirOSDataClass):
446460
frequency: int
447461
center1_freq: int
448462
dfs: int
449-
distance: int
463+
distance: int # In meters
450464
security: Security
451465
noisef: int
452466
txpower: int
@@ -554,6 +568,9 @@ class Derived(AirOSDataClass):
554568
ptp: bool
555569
ptmp: bool
556570

571+
role: DerivedWirelessRole
572+
mode: DerivedWirelessMode
573+
557574

558575
@dataclass
559576
class AirOS8Data(AirOSDataClass):

fixtures/airos_LiteBeam5AC_ap-ptp_30mhz.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
"access_point": true,
1414
"mac": "68:D7:9A:9A:08:BB",
1515
"mac_interface": "br0",
16+
"mode": "point_to_point",
1617
"ptmp": false,
1718
"ptp": true,
19+
"role": "access_point",
1820
"station": false
1921
},
2022
"firewall": {

fixtures/airos_LiteBeam5AC_sta-ptp_30mhz.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
"access_point": false,
1414
"mac": "68:D7:9A:98:FB:FF",
1515
"mac_interface": "br0",
16+
"mode": "point_to_point",
1617
"ptmp": false,
1718
"ptp": true,
19+
"role": "station",
1820
"station": true
1921
},
2022
"firewall": {

fixtures/airos_liteapgps_ap_ptmp_40mhz.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
"access_point": true,
1414
"mac": "04:11:22:33:19:7E",
1515
"mac_interface": "br0",
16+
"mode": "point_to_multipoint",
1617
"ptmp": true,
1718
"ptp": false,
19+
"role": "access_point",
1820
"station": false
1921
},
2022
"firewall": {

fixtures/airos_loco5ac_ap-ptp.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
"access_point": true,
1414
"mac": "01:23:45:67:89:AB",
1515
"mac_interface": "br0",
16+
"mode": "point_to_point",
1617
"ptmp": false,
1718
"ptp": true,
19+
"role": "access_point",
1820
"station": false
1921
},
2022
"firewall": {

fixtures/airos_loco5ac_sta-ptp.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
"access_point": false,
1414
"mac": "01:23:45:67:89:CD",
1515
"mac_interface": "br0",
16+
"mode": "point_to_point",
1617
"ptmp": false,
1718
"ptp": true,
19+
"role": "station",
1820
"station": true
1921
},
2022
"firewall": {

fixtures/airos_nanobeam5ac_sta_ptmp_40mhz.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
"access_point": false,
1414
"mac": "22:22:33:44:31:38",
1515
"mac_interface": "br0",
16+
"mode": "point_to_multipoint",
1617
"ptmp": true,
1718
"ptp": false,
19+
"role": "station",
1820
"station": true
1921
},
2022
"firewall": {

fixtures/airos_nanostation_ap-ptp_8718_missing_gps.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
"access_point": true,
1414
"mac": "00:11:22:33:34:66",
1515
"mac_interface": "br0",
16+
"mode": "point_to_point",
1617
"ptmp": false,
1718
"ptp": true,
19+
"role": "access_point",
1820
"station": false
1921
},
2022
"firewall": {

0 commit comments

Comments
 (0)