From 39b9ed9f41869aa5c744a66fa3d81928ac21f32e Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 3 May 2026 14:47:44 +0000 Subject: [PATCH 1/2] Add exception for legacy v6 --- CHANGELOG.md | 6 ++++++ airos/base.py | 22 ++++++++++++++++++++++ airos/exceptions.py | 4 ++++ pyproject.toml | 2 +- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49ead27..5b4db2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [0.6.6] - 2026-04-28 + +### Added + +- Introduce TLS adjustment for legacy v6 devices + ## [0.6.5] - 2026-04-28 ### Added diff --git a/airos/base.py b/airos/base.py index 2ee8c65..de2eee3 100644 --- a/airos/base.py +++ b/airos/base.py @@ -8,6 +8,7 @@ from http.cookies import SimpleCookie import json import logging +from ssl import SSLError from typing import Any, Generic, TypeVar from urllib.parse import urlparse @@ -27,6 +28,7 @@ AirOSDeviceConnectionError, AirOSKeyDataMissingError, AirOSMultipleMatchesFoundException, + AirOSTLSCompatibilityError, AirOSUrlNotFoundError, ) from .model_map import UispAirOSProductMapper @@ -308,6 +310,11 @@ async def _request_json( if err.status == 404: raise AirOSUrlNotFoundError from err raise AirOSConnectionSetupError from err + except aiohttp.ClientConnectorSSLError as err: + if _is_tls_compatibility_error(err): + _LOGGER.exception("TLS compatibility error during API call to %s", url) + raise AirOSTLSCompatibilityError from err + raise AirOSDeviceConnectionError from err except (TimeoutError, aiohttp.ClientError) as err: _LOGGER.exception("Error during API call to %s", url) raise AirOSDeviceConnectionError from err @@ -512,3 +519,18 @@ async def install(self) -> dict[str, Any]: ct_json=True, authenticated=True, ) + + +def _is_tls_compatibility_error(err: aiohttp.ClientConnectorSSLError) -> bool: + """Return True for known legacy airOS TLS handshake failures.""" + if "SSLV3_ALERT_HANDSHAKE_FAILURE" in str(err): + return True + + cause = err.__cause__ + if isinstance(cause, SSLError): + message = str(cause).lower() + return ( + "handshake failure" in message or "sslv3 alert handshake failure" in message + ) + + return False diff --git a/airos/exceptions.py b/airos/exceptions.py index 8783a0e..72467b8 100644 --- a/airos/exceptions.py +++ b/airos/exceptions.py @@ -47,3 +47,7 @@ class AirOSUrlNotFoundError(AirOSException): class AirOSMultipleMatchesFoundException(AirOSException): """Raised when multiple devices found for lookup.""" + + +class AirOSTLSCompatibilityError(AirOSDeviceConnectionError): + """The device requires legacy TLS/cipher compaitibility.""" diff --git a/pyproject.toml b/pyproject.toml index 7287793..2ed18ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.6.5" +version = "0.6.6a0" license = "MIT" description = "Ubiquiti airOS module(s) for Python 3." readme = "README.md" From 38e6efe8bce32ec49d8a059d84ca523c2424014c Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 3 May 2026 14:59:52 +0000 Subject: [PATCH 2/2] Add exception for legacy v6 --- airos/helpers.py | 4 ++++ pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/airos/helpers.py b/airos/helpers.py index 2c37cfa..cb13604 100644 --- a/airos/helpers.py +++ b/airos/helpers.py @@ -12,6 +12,7 @@ AirOSDataMissingError, AirOSDeviceConnectionError, AirOSKeyDataMissingError, + AirOSTLSCompatibilityError, ) _LOGGER = logging.getLogger(__name__) @@ -38,6 +39,9 @@ async def async_get_firmware_data( try: await detect_device.login() device_data = await detect_device.raw_status() + except AirOSTLSCompatibilityError: + _LOGGER.exception("TLS compatibility error during API call to %s", host) + raise except ( AirOSConnectionSetupError, AirOSDeviceConnectionError, diff --git a/pyproject.toml b/pyproject.toml index 2ed18ce..f694257 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.6.6a0" +version = "0.6.6ab" license = "MIT" description = "Ubiquiti airOS module(s) for Python 3." readme = "README.md"