From 13bd3b0e62147f27f5453824574b04e3622c5a57 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 8 Aug 2025 22:15:51 +0200 Subject: [PATCH 1/8] Update Changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc102b2..b22b7e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## [0.2.6] - 2025-08-08 + +### Added + +- Added support for 8.7.11 NanoStation not having 'age' in the 'remote'(s) +- Added debugging script for pinpointing issues in the dataclass + ## [0.2.6] - 2025-08-06 ### Added From 8c77a81c558bc3a6208b9d6d6232bad1f9ceac56 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 8 Aug 2025 23:34:14 +0200 Subject: [PATCH 2/8] Add ClientConnectError --- airos/airos8.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/airos/airos8.py b/airos/airos8.py index 0476509..3db8116 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -188,6 +188,7 @@ async def login(self) -> bool: except ( aiohttp.ClientError, aiohttp.client_exceptions.ConnectionTimeoutError, + aiohttp.client_exceptions.ClientConnectorError, ) as err: _LOGGER.exception("Error during login") raise AirOSDeviceConnectionError from err @@ -304,6 +305,7 @@ async def status(self) -> AirOSData: except ( aiohttp.ClientError, aiohttp.client_exceptions.ConnectionTimeoutError, + aiohttp.client_exceptions.ClientConnectorError, ) as err: _LOGGER.error("Status API call failed: %s", err) raise AirOSDeviceConnectionError from err @@ -342,6 +344,7 @@ async def stakick(self, mac_address: str = None) -> bool: except ( aiohttp.ClientError, aiohttp.client_exceptions.ConnectionTimeoutError, + aiohttp.client_exceptions.ClientConnectorError, ) as err: _LOGGER.exception("Error during reconnect request call") raise AirOSDeviceConnectionError from err @@ -381,6 +384,7 @@ async def provmode(self, active: bool = False) -> bool: except ( aiohttp.ClientError, aiohttp.client_exceptions.ConnectionTimeoutError, + aiohttp.client_exceptions.ClientConnectorError, ) as err: _LOGGER.exception("Error during provisioning mode call") raise AirOSDeviceConnectionError from err From a61c4fce303317140288942bd2f11d024c552889 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 8 Aug 2025 23:48:00 +0200 Subject: [PATCH 3/8] Improve exception handling --- airos/airos8.py | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/airos/airos8.py b/airos/airos8.py index 3db8116..6faaafa 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -2,6 +2,7 @@ from __future__ import annotations +import asyncio import json import logging from typing import Any @@ -185,13 +186,12 @@ async def login(self) -> bool: log = f"Login failed with status {response.status}. Full Response: {response.text}" _LOGGER.error(log) raise AirOSConnectionAuthenticationError from None - except ( - aiohttp.ClientError, - aiohttp.client_exceptions.ConnectionTimeoutError, - aiohttp.client_exceptions.ClientConnectorError, - ) as err: + except (TimeoutError, aiohttp.ClientError) as err: _LOGGER.exception("Error during login") raise AirOSDeviceConnectionError from err + except asyncio.CancelledError: + _LOGGER.info("Login task was cancelled") + raise def derived_data( self, response: dict[str, Any] | None = None @@ -302,13 +302,12 @@ async def status(self) -> AirOSData: response_text, ) raise AirOSDeviceConnectionError - except ( - aiohttp.ClientError, - aiohttp.client_exceptions.ConnectionTimeoutError, - aiohttp.client_exceptions.ClientConnectorError, - ) as err: - _LOGGER.error("Status API call failed: %s", err) + except (TimeoutError, aiohttp.ClientError) as err: + _LOGGER.exception("Status API call failed: %s", err) raise AirOSDeviceConnectionError from err + except asyncio.CancelledError: + _LOGGER.info("API status retrieval task was cancelled") + raise async def stakick(self, mac_address: str = None) -> bool: """Reconnect client station.""" @@ -341,13 +340,12 @@ async def stakick(self, mac_address: str = None) -> bool: log = f"Unable to restart connection response status {response.status} with {response_text}" _LOGGER.error(log) return False - except ( - aiohttp.ClientError, - aiohttp.client_exceptions.ConnectionTimeoutError, - aiohttp.client_exceptions.ClientConnectorError, - ) as err: - _LOGGER.exception("Error during reconnect request call") + except (TimeoutError, aiohttp.ClientError) as err: + _LOGGER.exception("Error during call to reconnect remote: %s", err) raise AirOSDeviceConnectionError from err + except asyncio.CancelledError: + _LOGGER.info("Reconnect task was cancelled") + raise async def provmode(self, active: bool = False) -> bool: """Set provisioning mode.""" @@ -381,10 +379,9 @@ async def provmode(self, active: bool = False) -> bool: log = f"Unable to change provisioning mode response status {response.status} with {response_text}" _LOGGER.error(log) return False - except ( - aiohttp.ClientError, - aiohttp.client_exceptions.ConnectionTimeoutError, - aiohttp.client_exceptions.ClientConnectorError, - ) as err: - _LOGGER.exception("Error during provisioning mode call") + except (TimeoutError, aiohttp.ClientError) as err: + _LOGGER.exception("Error during call to change provisioning mode: %s", err) raise AirOSDeviceConnectionError from err + except asyncio.CancelledError: + _LOGGER.info("Provisioning mode change task was cancelled") + raise From 14699d1a1dd3ee7c93eba2f89733b77e81e0d158 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Fri, 8 Aug 2025 23:50:04 +0200 Subject: [PATCH 4/8] Update changelog --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b22b7e4..29b3e93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ All notable changes to this project will be documented in this file. -## [0.2.6] - 2025-08-08 +## [0.2.8] - 2025-08-09 + +### Changed + +- Improved exception handling + +## [0.2.7] - 2025-08-08 ### Added From 5ad17542b0e8c9f79e34675bdbc9f6582e959844 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sat, 9 Aug 2025 09:00:29 +0200 Subject: [PATCH 5/8] Bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 085b13f..588d8f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.2.7" +version = "0.2.8a0" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md" From 8c34392586e213bdffbf5247f34a652e0e3ceb5a Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sat, 9 Aug 2025 09:43:46 +0200 Subject: [PATCH 6/8] Explicit timeout and version bump --- airos/airos8.py | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/airos/airos8.py b/airos/airos8.py index 6faaafa..6c4762b 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -186,7 +186,7 @@ async def login(self) -> bool: log = f"Login failed with status {response.status}. Full Response: {response.text}" _LOGGER.error(log) raise AirOSConnectionAuthenticationError from None - except (TimeoutError, aiohttp.ClientError) as err: + except (TimeoutError, aiohttp.client_exceptions.ClientError) as err: _LOGGER.exception("Error during login") raise AirOSDeviceConnectionError from err except asyncio.CancelledError: @@ -302,7 +302,7 @@ async def status(self) -> AirOSData: response_text, ) raise AirOSDeviceConnectionError - except (TimeoutError, aiohttp.ClientError) as err: + except (TimeoutError, aiohttp.client_exceptions.ClientError) as err: _LOGGER.exception("Status API call failed: %s", err) raise AirOSDeviceConnectionError from err except asyncio.CancelledError: @@ -340,7 +340,7 @@ async def stakick(self, mac_address: str = None) -> bool: log = f"Unable to restart connection response status {response.status} with {response_text}" _LOGGER.error(log) return False - except (TimeoutError, aiohttp.ClientError) as err: + except (TimeoutError, aiohttp.client_exceptions.ClientError) as err: _LOGGER.exception("Error during call to reconnect remote: %s", err) raise AirOSDeviceConnectionError from err except asyncio.CancelledError: @@ -379,7 +379,7 @@ async def provmode(self, active: bool = False) -> bool: log = f"Unable to change provisioning mode response status {response.status} with {response_text}" _LOGGER.error(log) return False - except (TimeoutError, aiohttp.ClientError) as err: + except (TimeoutError, aiohttp.client_exceptions.ClientError) as err: _LOGGER.exception("Error during call to change provisioning mode: %s", err) raise AirOSDeviceConnectionError from err except asyncio.CancelledError: diff --git a/pyproject.toml b/pyproject.toml index 588d8f7..a924b57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.2.8a0" +version = "0.2.8a4" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md" From 7d86400833a125447219bdd3596fecbe3bad99f2 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Tue, 12 Aug 2025 16:17:08 +0200 Subject: [PATCH 7/8] Optional GPS data --- CHANGELOG.md | 3 +- airos/data.py | 8 +- fixtures/airos_liteapgps_ap_ptmp_40mhz.json | 7 +- fixtures/airos_loco5ac_ap-ptp.json | 7 +- fixtures/airos_loco5ac_sta-ptp.json | 7 +- .../airos_nanobeam5ac_sta_ptmp_40mhz.json | 7 +- ...s_nanostation_ap-ptp_8718_missing_gps.json | 611 ++++++++++++++++++ .../nanostation_ap-ptp_8718_missing_gps.json | 2 + pyproject.toml | 2 +- tests/test_stations.py | 1 + 10 files changed, 647 insertions(+), 8 deletions(-) create mode 100644 fixtures/airos_nanostation_ap-ptp_8718_missing_gps.json create mode 100644 fixtures/userdata/nanostation_ap-ptp_8718_missing_gps.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 29b3e93..eaf8e10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,12 @@ All notable changes to this project will be documented in this file. -## [0.2.8] - 2025-08-09 +## [0.2.8] - 2025-08-12 ### Changed - Improved exception handling +- GPS data optional (reported on NanoStation via HA Core Issue 150491) ## [0.2.7] - 2025-08-08 diff --git a/airos/data.py b/airos/data.py index 80d66a3..d3377f8 100644 --- a/airos/data.py +++ b/airos/data.py @@ -348,7 +348,6 @@ class Remote(AirOSDataClass): cable_loss: int ethlist: list[EthList] ipaddr: list[str] - gps: GPSData oob: bool unms: UnmsStatus airview: int @@ -357,6 +356,9 @@ class Remote(AirOSDataClass): ip6addr: list[str] | None = None # For v4 only devices height: int | None = None age: int | None = None # At least not present on 8.7.11 + gps: GPSData | None = ( + None # Reported NanoStation 5AC 8.7.18 without GPS Core 150491 + ) @classmethod def __pre_deserialize__(cls, d: dict[str, Any]) -> dict[str, Any]: @@ -556,5 +558,7 @@ class AirOS8Data(AirOSDataClass): provmode: Any ntpclient: Any unms: UnmsStatus - gps: GPSMain derived: Derived + gps: GPSData | None = ( + None # Reported NanoStation 5AC 8.7.18 without GPS Core 150491 + ) diff --git a/fixtures/airos_liteapgps_ap_ptmp_40mhz.json b/fixtures/airos_liteapgps_ap_ptmp_40mhz.json index f3b2824..d11415f 100644 --- a/fixtures/airos_liteapgps_ap_ptmp_40mhz.json +++ b/fixtures/airos_liteapgps_ap_ptmp_40mhz.json @@ -25,9 +25,14 @@ }, "genuine": "/images/genuine.png", "gps": { + "alt": 252.5, + "dim": 3, + "dop": 1.52, "fix": 1, "lat": 52.379894, - "lon": 4.901608 + "lon": 4.901608, + "sats": 8, + "time_synced": null }, "host": { "cpuload": 59.595959, diff --git a/fixtures/airos_loco5ac_ap-ptp.json b/fixtures/airos_loco5ac_ap-ptp.json index 23622e3..6f57d5d 100644 --- a/fixtures/airos_loco5ac_ap-ptp.json +++ b/fixtures/airos_loco5ac_ap-ptp.json @@ -25,9 +25,14 @@ }, "genuine": "/images/genuine.png", "gps": { + "alt": null, + "dim": null, + "dop": null, "fix": 0, "lat": 52.379894, - "lon": 4.901608 + "lon": 4.901608, + "sats": null, + "time_synced": null }, "host": { "cpuload": 10.10101, diff --git a/fixtures/airos_loco5ac_sta-ptp.json b/fixtures/airos_loco5ac_sta-ptp.json index 4b65c20..932102e 100644 --- a/fixtures/airos_loco5ac_sta-ptp.json +++ b/fixtures/airos_loco5ac_sta-ptp.json @@ -25,9 +25,14 @@ }, "genuine": "/images/genuine.png", "gps": { + "alt": null, + "dim": null, + "dop": null, "fix": 0, "lat": 52.379894, - "lon": 4.901608 + "lon": 4.901608, + "sats": null, + "time_synced": null }, "host": { "cpuload": 44.0, diff --git a/fixtures/airos_nanobeam5ac_sta_ptmp_40mhz.json b/fixtures/airos_nanobeam5ac_sta_ptmp_40mhz.json index a6df0eb..5ec968b 100644 --- a/fixtures/airos_nanobeam5ac_sta_ptmp_40mhz.json +++ b/fixtures/airos_nanobeam5ac_sta_ptmp_40mhz.json @@ -25,9 +25,14 @@ }, "genuine": "/images/genuine.png", "gps": { + "alt": null, + "dim": null, + "dop": null, "fix": 0, "lat": 52.379894, - "lon": 4.901608 + "lon": 4.901608, + "sats": null, + "time_synced": null }, "host": { "cpuload": 32.673267, diff --git a/fixtures/airos_nanostation_ap-ptp_8718_missing_gps.json b/fixtures/airos_nanostation_ap-ptp_8718_missing_gps.json new file mode 100644 index 0000000..948f514 --- /dev/null +++ b/fixtures/airos_nanostation_ap-ptp_8718_missing_gps.json @@ -0,0 +1,611 @@ +{ + "chain_names": [ + { + "name": "Chain 0", + "number": 1 + }, + { + "name": "Chain 1", + "number": 2 + } + ], + "derived": { + "access_point": true, + "mac": "00:11:22:33:34:66", + "mac_interface": "br0", + "ptmp": false, + "ptp": true, + "station": false + }, + "firewall": { + "eb6tables": false, + "ebtables": false, + "ip6tables": false, + "iptables": false + }, + "genuine": "/images/genuine.png", + "gps": null, + "host": { + "cpuload": 26.0, + "device_id": "REDACTED", + "devmodel": "NanoStation 5AC loco", + "freeram": 17162240, + "fwversion": "v8.7.18", + "height": null, + "hostname": "REDACTED", + "loadavg": 0.0, + "netrole": "bridge", + "power_time": 3654339, + "temperature": 0, + "time": "2025-07-30 07:48:18", + "timestamp": 1507841469, + "totalram": 63447040, + "uptime": 161904 + }, + "interfaces": [ + { + "enabled": true, + "hwaddr": "00:11:22:33:34:66", + "ifname": "eth0", + "mtu": 1500, + "status": { + "cable_len": 14, + "duplex": true, + "ip6addr": null, + "ipaddr": "127.0.0.3", + "plugged": true, + "rx_bytes": 829225952, + "rx_dropped": 0, + "rx_errors": 320, + "rx_packets": 2163281, + "snr": [ + 29, + 29, + 28, + 27 + ], + "speed": 1000, + "tx_bytes": 264634988, + "tx_dropped": 0, + "tx_errors": 0, + "tx_packets": 842398 + } + }, + { + "enabled": true, + "hwaddr": "00:11:22:33:34:66", + "ifname": "ath0", + "mtu": 1500, + "status": { + "cable_len": null, + "duplex": false, + "ip6addr": null, + "ipaddr": "127.0.0.3", + "plugged": false, + "rx_bytes": 277502186, + "rx_dropped": 0, + "rx_errors": 0, + "rx_packets": 795364, + "snr": null, + "speed": 0, + "tx_bytes": 964027431, + "tx_dropped": 1310, + "tx_errors": 0, + "tx_packets": 2193278 + } + }, + { + "enabled": true, + "hwaddr": "00:11:22:33:34:66", + "ifname": "br0", + "mtu": 1500, + "status": { + "cable_len": null, + "duplex": false, + "ip6addr": null, + "ipaddr": "127.0.0.3", + "plugged": true, + "rx_bytes": 308938052, + "rx_dropped": 0, + "rx_errors": 0, + "rx_packets": 1465113, + "snr": null, + "speed": 0, + "tx_bytes": 41845082, + "tx_dropped": 0, + "tx_errors": 0, + "tx_packets": 173005 + } + } + ], + "ntpclient": {}, + "portfw": false, + "provmode": {}, + "services": { + "airview": 2, + "dhcp6d_stateful": false, + "dhcpc": true, + "dhcpd": false, + "pppoe": false + }, + "unms": { + "status": 0, + "timestamp": null + }, + "wireless": { + "antenna_gain": 13, + "apmac": "00:11:22:33:34:66", + "aprepeater": false, + "band": 2, + "cac_state": 0, + "cac_timeout": 0, + "center1_freq": 5550, + "chanbw": 80, + "compat_11n": 0, + "count": 1, + "dfs": 1, + "distance": 1200, + "essid": "REDACTED", + "frequency": 5520, + "hide_essid": 0, + "ieeemode": "11ACVHT80", + "mode": "ap-ptp", + "noisef": -89, + "nol_state": 0, + "nol_timeout": 0, + "polling": { + "atpc_status": 0, + "cb_capacity": 198315, + "dl_capacity": 182520, + "ff_cap_rep": false, + "fixed_frame": false, + "flex_mode": null, + "gps_sync": false, + "rx_use": 1, + "tx_use": 0, + "ul_capacity": 214110, + "use": 1 + }, + "rstatus": 5, + "rx_chainmask": 3, + "rx_idx": 4, + "rx_nss": 2, + "security": "WPA2", + "service": { + "link": 3650562, + "time": 3654144 + }, + "sta": [ + { + "airmax": { + "actual_priority": 0, + "atpc_status": 0, + "beam": 0, + "cb_capacity": 198315, + "desired_priority": 0, + "dl_capacity": 182520, + "rx": { + "cinr": 20, + "evm": [ + [ + 21, + 19, + 19, + 21, + 22, + 20, + 21, + 19, + 20, + 21, + 21, + 19, + 20, + 19, + 21, + 21, + 19, + 20, + 23, + 19, + 21, + 21, + 20, + 20, + 21, + 19, + 22, + 19, + 23, + 18, + 23, + 19, + 18, + 17, + 21, + 20, + 20, + 19, + 19, + 18, + 22, + 21, + 19, + 20, + 20, + 19, + 19, + 22, + 21, + 20, + 20, + 20, + 21, + 20, + 19, + 21, + 18, + 21, + 20, + 19, + 20, + 22, + 19, + 19 + ], + [ + 16, + 16, + 15, + 15, + 16, + 16, + 16, + 16, + 16, + 16, + 15, + 15, + 16, + 15, + 16, + 16, + 16, + 15, + 17, + 16, + 17, + 15, + 16, + 17, + 15, + 15, + 16, + 17, + 15, + 15, + 15, + 15, + 15, + 15, + 16, + 15, + 14, + 15, + 15, + 15, + 16, + 19, + 15, + 17, + 15, + 16, + 15, + 18, + 16, + 16, + 19, + 16, + 16, + 15, + 15, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 17, + 16 + ] + ], + "usage": 1 + }, + "tx": { + "cinr": 20, + "evm": [ + [ + 19, + 21, + 18, + 19, + 21, + 21, + 19, + 18, + 18, + 20, + 21, + 19, + 22, + 22, + 19, + 20, + 17, + 19, + 21, + 20, + 19, + 20, + 20, + 18, + 19, + 19, + 19, + 20, + 21, + 18, + 20, + 22, + 19, + 20, + 21, + 19, + 20, + 21, + 20, + 20, + 21, + 19, + 20, + 20, + 21, + 19, + 22, + 20, + 19, + 20, + 19, + 20, + 20, + 19, + 21, + 20, + 21, + 18, + 20, + 18, + 20, + 23, + 21, + 20 + ], + [ + 16, + 18, + 16, + 15, + 16, + 15, + 16, + 16, + 15, + 15, + 15, + 15, + 16, + 15, + 15, + 16, + 15, + 16, + 16, + 16, + 15, + 15, + 16, + 15, + 16, + 17, + 15, + 16, + 16, + 16, + 14, + 16, + 16, + 15, + 16, + 16, + 15, + 15, + 16, + 16, + 16, + 15, + 15, + 16, + 15, + 15, + 15, + 15, + 15, + 15, + 16, + 15, + 16, + 15, + 15, + 16, + 15, + 16, + 15, + 16, + 16, + 15, + 15, + 15 + ] + ], + "usage": 0 + }, + "ul_capacity": 214110 + }, + "airos_connected": true, + "cb_capacity_expect": 416000, + "chainrssi": [ + 18, + 18, + 0 + ], + "distance": 1200, + "dl_avg_linkscore": 49, + "dl_capacity_expect": 416000, + "dl_linkscore": 43, + "dl_rate_expect": 5, + "dl_signal_expect": -67, + "last_disc": 3, + "lastip": "127.0.0.3", + "mac": "00:11:22:33:14:87", + "noisefloor": -89, + "remote": { + "age": 3, + "airview": 2, + "antenna_gain": 13, + "cable_loss": 0, + "chainrssi": [ + 18, + 17, + 0 + ], + "compat_11n": 0, + "cpuload": 1.9801, + "device_id": "REDACTED", + "distance": 1200, + "ethlist": [ + { + "cable_len": 18, + "duplex": true, + "enabled": true, + "ifname": "eth0", + "plugged": true, + "snr": [ + 29, + 29, + 26, + 26 + ], + "speed": 1000 + } + ], + "freeram": 22302720, + "gps": null, + "height": null, + "hostname": "REDACTED", + "ip6addr": [ + "127.0.0.3" + ], + "ipaddr": [ + "127.0.0.3" + ], + "mode": "sta-ptp", + "netrole": "router", + "noisefloor": -89, + "oob": false, + "platform": "REDACTED", + "power_time": 3703848, + "rssi": 21, + "rx_bytes": 878525330, + "rx_chainmask": 3, + "rx_throughput": 1, + "service": { + "link": 3696241, + "time": 3703776 + }, + "signal": -75, + "sys_id": "REDACTED", + "temperature": 0, + "time": "2025-08-12 14:42:13", + "totalram": 63447040, + "tx_bytes": 306718727, + "tx_power": 17, + "tx_ratedata": [ + 2, + 4, + 7, + 7043, + 514748, + 106809, + 2751, + 3, + 0, + 0 + ], + "tx_throughput": 0, + "unms": { + "status": 1, + "timestamp": null + }, + "uptime": 161938, + "version": "WA.ar934x.v8.7.18.48247.250728.0850" + }, + "rssi": 21, + "rx_idx": 4, + "rx_nss": 2, + "signal": -75, + "stats": { + "rx_bytes": 230970630, + "rx_packets": 653958, + "rx_pps": 3, + "tx_bytes": 788129637, + "tx_packets": 1766581, + "tx_pps": 0 + }, + "tx_idx": 4, + "tx_latency": 8, + "tx_lretries": 0, + "tx_nss": 2, + "tx_packets": 0, + "tx_ratedata": [ + 6738, + 4, + 4, + 4351, + 1181709, + 293024, + 18324, + 2, + 0, + 0 + ], + "tx_sretries": 0, + "ul_avg_linkscore": 54, + "ul_capacity_expect": 416000, + "ul_linkscore": 51, + "ul_rate_expect": 5, + "ul_signal_expect": -67, + "uptime": 128050 + } + ], + "sta_disconnected": [], + "throughput": { + "rx": 1, + "tx": 17 + }, + "tx_chainmask": 3, + "tx_idx": 4, + "tx_nss": 2, + "txpower": 17 + } +} \ No newline at end of file diff --git a/fixtures/userdata/nanostation_ap-ptp_8718_missing_gps.json b/fixtures/userdata/nanostation_ap-ptp_8718_missing_gps.json new file mode 100644 index 0000000..e04be4b --- /dev/null +++ b/fixtures/userdata/nanostation_ap-ptp_8718_missing_gps.json @@ -0,0 +1,2 @@ +{"chain_names": [{"number": 1, "name": "Chain 0"}, {"number": 2, "name": "Chain 1"}], "host": {"hostname": "REDACTED", "device_id": "REDACTED", "uptime": 161904, "power_time": 3654339, "time": "2025-07-30 07:48:18", "timestamp": 1507841469, "fwversion": "v8.7.18", "devmodel": "NanoStation 5AC loco", "netrole": "bridge", "loadavg": 0.0, "totalram": 63447040, "freeram": 17162240, "temperature": 0, "cpuload": 26.0, "height": null}, "genuine": "/images/genuine.png", "services": {"dhcpc": true, "dhcpd": false, "dhcp6d_stateful": false, "pppoe": false, "airview": 2}, "firewall": {"iptables": false, "ebtables": false, "ip6tables": false, "eb6tables": false}, "portfw": false, "wireless": {"essid": "REDACTED", "mode": "ap-ptp", "ieeemode": "11ACVHT80", "band": 2, "compat_11n": 0, "hide_essid": 0, "apmac": "00:11:22:33:34:66", "antenna_gain": 13, "frequency": 5520, "center1_freq": 5550, "dfs": 1, "distance": 1200, "security": "WPA2", "noisef": -89, "txpower": 17, "aprepeater": false, "rstatus": 5, "chanbw": 80, "rx_chainmask": 3, "tx_chainmask": 3, "nol_state": 0, "nol_timeout": 0, "cac_state": 0, "cac_timeout": 0, "rx_idx": 4, "rx_nss": 2, "tx_idx": 4, "tx_nss": 2, "throughput": {"tx": 17, "rx": 1}, "service": {"time": 3654144, "link": 3650562}, "polling": {"cb_capacity": 198315, "dl_capacity": 182520, "ul_capacity": 214110, "use": 1, "tx_use": 0, "rx_use": 1, "atpc_status": 0, "fixed_frame": false, "gps_sync": false, "ff_cap_rep": false}, "count": 1, "sta": [{"mac": "00:11:22:33:14:87", "lastip": "127.0.0.3", "signal": -75, "rssi": 21, "noisefloor": -89, "chainrssi": [18, 18, 0], "tx_idx": 4, "rx_idx": 4, "tx_nss": 2, "rx_nss": 2, "tx_latency": 8, "distance": 1200, "tx_packets": 0, "tx_lretries": 0, "tx_sretries": 0, "uptime": 128050, "dl_signal_expect": -67, "ul_signal_expect": -67, "cb_capacity_expect": 416000, "dl_capacity_expect": 416000, "ul_capacity_expect": 416000, "dl_rate_expect": 5, "ul_rate_expect": 5, "dl_linkscore": 43, "ul_linkscore": 51, "dl_avg_linkscore": 49, "ul_avg_linkscore": 54, "tx_ratedata": [6738, 4, 4, 4351, 1181709, 293024, 18324, 2, 0, 0], "stats": {"rx_bytes": 230970630, "rx_packets": 653958, "rx_pps": 3, "tx_bytes": 788129637, "tx_packets": 1766581, "tx_pps": 0}, "airmax": {"actual_priority": 0, "beam": 0, "desired_priority": 0, "cb_capacity": 198315, "dl_capacity": 182520, "ul_capacity": 214110, "atpc_status": 0, "rx": {"usage": 1, "cinr": 20, "evm": [[21, 19, 19, 21, 22, 20, 21, 19, 20, 21, 21, 19, 20, 19, 21, 21, 19, 20, 23, 19, 21, 21, 20, 20, 21, 19, 22, 19, 23, 18, 23, 19, 18, 17, 21, 20, 20, 19, 19, 18, 22, 21, 19, 20, 20, 19, 19, 22, 21, 20, 20, 20, 21, 20, 19, 21, 18, 21, 20, 19, 20, 22, 19, 19], [16, 16, 15, 15, 16, 16, 16, 16, 16, 16, 15, 15, 16, 15, 16, 16, 16, 15, 17, 16, 17, 15, 16, 17, 15, 15, 16, 17, 15, 15, 15, 15, 15, 15, 16, 15, 14, 15, 15, 15, 16, 19, 15, 17, 15, 16, 15, 18, 16, 16, 19, 16, 16, 15, 15, 16, 16, 16, 16, 16, 16, 16, 17, 16]]}, "tx": {"usage": 0, "cinr": 20, "evm": [[19, 21, 18, 19, 21, 21, 19, 18, 18, 20, 21, 19, 22, 22, 19, 20, 17, 19, 21, 20, 19, 20, 20, 18, 19, 19, 19, 20, 21, 18, 20, 22, 19, 20, 21, 19, 20, 21, 20, 20, 21, 19, 20, 20, 21, 19, 22, 20, 19, 20, 19, 20, 20, 19, 21, 20, 21, 18, 20, 18, 20, 23, 21, 20], [16, 18, 16, 15, 16, 15, 16, 16, 15, 15, 15, 15, 16, 15, 15, 16, 15, 16, 16, 16, 15, 15, 16, 15, 16, 17, 15, 16, 16, 16, 14, 16, 16, 15, 16, 16, 15, 15, 16, 16, 16, 15, 15, 16, 15, 15, 15, 15, 15, 15, 16, 15, 16, 15, 15, 16, 15, 16, 15, 16, 16, 15, 15, 15]]}}, "last_disc": 3, "remote": {"age": 3, "device_id": "REDACTED", "hostname": "REDACTED", "platform": "REDACTED", "version": "WA.ar934x.v8.7.18.48247.250728.0850", "time": "2025-08-12 14:42:13", "cpuload": 1.9801, "temperature": 0, "totalram": 63447040, "freeram": 22302720, "netrole": "router", "mode": "sta-ptp", "sys_id": "REDACTED", "tx_throughput": 0, "rx_throughput": 1, "uptime": 161938, "power_time": 3703848, "compat_11n": 0, "signal": -75, "rssi": 21, "noisefloor": -89, "tx_power": 17, "distance": 1200, "rx_chainmask": 3, "chainrssi": [18, 17, 0], "tx_ratedata": [2, 4, 7, 7043, 514748, 106809, 2751, 3, 0, 0], "tx_bytes": 306718727, "rx_bytes": 878525330, "antenna_gain": 13, "cable_loss": 0, "height": null, "ethlist": [{"ifname": "eth0", "enabled": true, "plugged": true, "duplex": true, "speed": 1000, "snr": [29, 29, 26, 26], "cable_len": 18}], "ipaddr": ["127.0.0.3"], "ip6addr": ["127.0.0.3"], "oob": false, "unms": {"status": 1}, "airview": 2, "service": {"time": 3703776, "link": 3696241}}}], "sta_disconnected": []}, "interfaces": [{"ifname": "eth0", "hwaddr": "00:11:22:33:34:66", "enabled": true, "mtu": 1500, "status": {"plugged": true, "tx_bytes": 264634988, "rx_bytes": 829225952, "tx_packets": 842398, "rx_packets": 2163281, "tx_errors": 0, "rx_errors": 320, "tx_dropped": 0, "rx_dropped": 0, "ipaddr": "127.0.0.3", "speed": 1000, "duplex": true, "snr": [29, 29, 28, 27], "cable_len": 14}}, {"ifname": "ath0", "hwaddr": "00:11:22:33:34:66", "enabled": true, "mtu": 1500, "status": {"plugged": false, "tx_bytes": 964027431, "rx_bytes": 277502186, "tx_packets": 2193278, "rx_packets": 795364, "tx_errors": 0, "rx_errors": 0, "tx_dropped": 1310, "rx_dropped": 0, "ipaddr": "127.0.0.3", "speed": 0, "duplex": false}}, {"ifname": "br0", "hwaddr": "00:11:22:33:34:66", "enabled": true, "mtu": 1500, "status": {"plugged": true, "tx_bytes": 41845082, "rx_bytes": 308938052, "tx_packets": 173005, "rx_packets": 1465113, "tx_errors": 0, "rx_errors": 0, "tx_dropped": 0, "rx_dropped": 0, "ipaddr": "127.0.0.3", "speed": 0, "duplex": false}}], "provmode": {}, "ntpclient": {}, "unms": {"status": 0}, "derived": {"station": false, "access_point": true, "ptp": true, "ptmp": false, "mac": "00:11:22:33:34:66", "mac_interface": "br0"}} + diff --git a/pyproject.toml b/pyproject.toml index a924b57..7425abd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.2.8a4" +version = "0.2.8a5" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md" diff --git a/tests/test_stations.py b/tests/test_stations.py index dadddf5..2c64ba7 100644 --- a/tests/test_stations.py +++ b/tests/test_stations.py @@ -135,6 +135,7 @@ async def test_status_logs_exception_on_missing_field(mock_logger, airos_device) ("mode", "fixture"), [ ("ap-ptp", "loco5ac_ap-ptp"), + ("ap-ptp", "nanostation_ap-ptp_8718_missing_gps"), ("sta-ptp", "loco5ac_sta-ptp"), ("sta-ptmp", "mocked_sta-ptmp"), ("ap-ptmp", "liteapgps_ap_ptmp_40mhz"), From 475dc2466ee9a5ba6e84b4b0fff9f60734c49703 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Tue, 12 Aug 2025 18:17:33 +0200 Subject: [PATCH 8/8] Bump version for release --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7425abd..a967d6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.2.8a5" +version = "0.2.8" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md"