From c30bffc862bb5d48482b43afd16fadbb5cade3d1 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 20 Jul 2025 12:30:36 +0200 Subject: [PATCH 1/8] Functional stakick progress, version bump a0 --- airos/airos8.py | 3 +++ pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/airos/airos8.py b/airos/airos8.py index 7eaa82b..9df001d 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -251,6 +251,9 @@ async def stakick(self, mac_address: str = None) -> bool: ) as response: if response.status == 200: return True + response_text = await response.text() + log = f"Unable to restart connection response status {response.status} with {response_text}" + logger.error(log) return False except ( aiohttp.ClientError, diff --git a/pyproject.toml b/pyproject.toml index 699af56..6bff75b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.0.9" +version = "0.0.10a0" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md" From dd2fb9582a3af9242811d240ba15cc92f9f549bc Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 20 Jul 2025 12:39:21 +0200 Subject: [PATCH 2/8] Functional stakick progress, version bump a1 --- airos/airos8.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/airos/airos8.py b/airos/airos8.py index 9df001d..1332275 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -252,7 +252,7 @@ async def stakick(self, mac_address: str = None) -> bool: if response.status == 200: return True response_text = await response.text() - log = f"Unable to restart connection response status {response.status} with {response_text}" + log = f"Unable to restart connection response status {response.status} with {response_text} called with payload {post_data}" logger.error(log) return False except ( diff --git a/pyproject.toml b/pyproject.toml index 6bff75b..21c602d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.0.10a0" +version = "0.0.10a1" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md" From 839c2fd4904467a1ce085322c9e62ebcee737918 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 20 Jul 2025 12:58:49 +0200 Subject: [PATCH 3/8] Functional stakick progress, version bump a2 --- airos/airos8.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airos/airos8.py b/airos/airos8.py index 1332275..0b16d3f 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -252,7 +252,7 @@ async def stakick(self, mac_address: str = None) -> bool: if response.status == 200: return True response_text = await response.text() - log = f"Unable to restart connection response status {response.status} with {response_text} called with payload {post_data}" + log = f"Unable to restart connection response status {response.status} with {response_text} called with payload {post_data} with headers {kick_request_headers} and url {self._stakick_cgi_url}" logger.error(log) return False except ( From fd65378023f37e9977360af6afbe6f57c5c9915a Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 20 Jul 2025 13:01:25 +0200 Subject: [PATCH 4/8] Functional stakick progress, version bump a2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 21c602d..526f7e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.0.10a1" +version = "0.0.10a2" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md" From 5b02fccfd74269a94fdc7257da786662af4be7c4 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 20 Jul 2025 13:14:08 +0200 Subject: [PATCH 5/8] Functional stakick progress, version bump a3 --- airos/airos8.py | 2 ++ pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/airos/airos8.py b/airos/airos8.py index 0b16d3f..e49e565 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -228,6 +228,8 @@ async def stakick(self, mac_address: str = None) -> bool: logger.error("Device mac-address missing") raise DataMissingError from None + self.session.cookie_jar.update_cookies({"ok": "1"}) + # --- Step 2: Verify authenticated access by fetching status.cgi --- kick_request_headers = {**self._common_headers} if self.current_csrf_token: diff --git a/pyproject.toml b/pyproject.toml index 526f7e8..ba1bec6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.0.10a2" +version = "0.0.10a3" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md" From 5ed3d48f45d8bdfab8c2bb4fb952ef4d6ed0839e Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 20 Jul 2025 17:14:16 +0200 Subject: [PATCH 6/8] Functional stakick progress, version bump a4 --- airos/airos8.py | 4 +--- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/airos/airos8.py b/airos/airos8.py index e49e565..0c8e49d 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -228,9 +228,6 @@ async def stakick(self, mac_address: str = None) -> bool: logger.error("Device mac-address missing") raise DataMissingError from None - self.session.cookie_jar.update_cookies({"ok": "1"}) - - # --- Step 2: Verify authenticated access by fetching status.cgi --- kick_request_headers = {**self._common_headers} if self.current_csrf_token: kick_request_headers["X-CSRF-ID"] = self.current_csrf_token @@ -239,6 +236,7 @@ async def stakick(self, mac_address: str = None) -> bool: "staif": "ath0", "staid": quote(mac_address.upper(), safe=""), } + logger.error(kick_payload) kick_request_headers["Content-Type"] = ( "application/x-www-form-urlencoded; charset=UTF-8" diff --git a/pyproject.toml b/pyproject.toml index ba1bec6..8fb69f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.0.10a3" +version = "0.0.10a4" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md" From dbb0037e769a63393ca97f81c98c9c00bca2e8f9 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 20 Jul 2025 17:38:24 +0200 Subject: [PATCH 7/8] Functional stakick progress, version bump a5 --- airos/airos8.py | 9 +++------ pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/airos/airos8.py b/airos/airos8.py index 0c8e49d..4e152b8 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -4,7 +4,7 @@ import json import logging -from urllib.parse import quote, urlparse +from urllib.parse import urlparse import aiohttp @@ -232,10 +232,7 @@ async def stakick(self, mac_address: str = None) -> bool: if self.current_csrf_token: kick_request_headers["X-CSRF-ID"] = self.current_csrf_token - kick_payload = { - "staif": "ath0", - "staid": quote(mac_address.upper(), safe=""), - } + kick_payload = {"staif": "ath0", "staid": mac_address.upper()} logger.error(kick_payload) kick_request_headers["Content-Type"] = ( @@ -252,7 +249,7 @@ async def stakick(self, mac_address: str = None) -> bool: if response.status == 200: return True response_text = await response.text() - log = f"Unable to restart connection response status {response.status} with {response_text} called with payload {post_data} with headers {kick_request_headers} and url {self._stakick_cgi_url}" + log = f"Unable to restart connection response status {response.status} with {response_text}" logger.error(log) return False except ( diff --git a/pyproject.toml b/pyproject.toml index 8fb69f0..5f00513 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.0.10a4" +version = "0.0.10a5" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md" From c2749e47806ac5e141c21c9a2473f368c30efca7 Mon Sep 17 00:00:00 2001 From: Tom Scholten Date: Sun, 20 Jul 2025 17:45:57 +0200 Subject: [PATCH 8/8] Functional stakick progress, version bump .1 --- README.md | 34 ++++++++++++++++++++++++++++++++++ airos/airos8.py | 1 - pyproject.toml | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 43cf59e..dedd61b 100644 --- a/README.md +++ b/README.md @@ -1 +1,35 @@ # Ubiquity airOS Module + +## Main usage + +Via [Home-Assistant](https://www.home-assistant.io) - initial core integration [pending](https://github.com/home-assistant/core/pull/148989). + +## Working + +Emulating client browser + +```example.py +from airos.airos8 import AirOS +import aiohttp +import asyncio + +async def test_airos(): + session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(verify_ssl=False)) + device = AirOS(host="192.168.1.2",username="ubnt",password="password",session=session) + # Login + result = await device.login() + print(f"Result: {result}") + # Fetch status (large dict, including connected stations) + result = await device.status() + print(f"Result: {result}") + # Reconnect 'other side' + result = await device.stakick("01:23:45:67:89:AB") + print(f"Result: {result}") + +def async_loop(loop: asyncio.AbstractEventLoop) -> int: + return loop.run_until_complete(test_airos()) + +if __name__ == "__main__": + loop = asyncio.new_event_loop() + result = async_loop(loop) +``` diff --git a/airos/airos8.py b/airos/airos8.py index 4e152b8..d69b677 100644 --- a/airos/airos8.py +++ b/airos/airos8.py @@ -233,7 +233,6 @@ async def stakick(self, mac_address: str = None) -> bool: kick_request_headers["X-CSRF-ID"] = self.current_csrf_token kick_payload = {"staif": "ath0", "staid": mac_address.upper()} - logger.error(kick_payload) kick_request_headers["Content-Type"] = ( "application/x-www-form-urlencoded; charset=UTF-8" diff --git a/pyproject.toml b/pyproject.toml index 5f00513..7c2ac17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "airos" -version = "0.0.10a5" +version = "0.1.0" license = "MIT" description = "Ubiquity airOS module(s) for Python 3." readme = "README.md"