Skip to content

Commit

Permalink
1.13.0 (#537)
Browse files Browse the repository at this point in the history
* Bump actions/setup-python from 5.1.1 to 5.2.0 (#531)

* Close aiohttp session when deleting a `Connection` (#532)

* Add AsusData.DSL for dsllog_dataratedown/up (#530)

* Define DSL models by `Endpoint.DSL` (#533)

* Bump actions/upload-artifact from 4.3.6 to 4.4.0 (#534)

* Improve `Connection` initialization (#535)

* Bump python-dateutil to `2.9.0.post0` (#536)

* Bump version to `1.13.0`

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: GaryHuang-ASUS <[email protected]>
  • Loading branch information
3 people authored Sep 7, 2024
1 parent 34ef067 commit e2a1e0d
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 33 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
uses: actions/[email protected]

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5.1.1
uses: actions/setup-python@v5.2.0
with:
python-version: ${{ matrix.python-version }}

Expand Down Expand Up @@ -63,7 +63,7 @@ jobs:
uses: actions/[email protected]

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5.1.1
uses: actions/setup-python@v5.2.0
with:
python-version: ${{ matrix.python-version }}

Expand Down Expand Up @@ -92,7 +92,7 @@ jobs:
pytest --cov=asusrouter --cov-report=xml:unit-tests-cov-${{ matrix.python-version }}.xml -k 'not test_devices'
- name: Upload coverage to artifacts
uses: actions/upload-artifact@v4.3.6
uses: actions/upload-artifact@v4.4.0
with:
name: unit-tests-cov-${{ matrix.python-version }}
path: unit-tests-cov-${{ matrix.python-version }}.xml
Expand All @@ -109,7 +109,7 @@ jobs:
uses: actions/[email protected]

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5.1.1
uses: actions/setup-python@v5.2.0
with:
python-version: ${{ matrix.python-version }}

Expand Down Expand Up @@ -138,7 +138,7 @@ jobs:
pytest --cov=asusrouter --cov-report=xml:real-data-tests-cov-${{ matrix.python-version }}.xml tests/test_devices.py --log-cli-level=INFO
- name: Upload coverage to artifacts
uses: actions/upload-artifact@v4.3.6
uses: actions/upload-artifact@v4.4.0
with:
name: real-data-tests-cov-${{ matrix.python-version }}
path: real-data-tests-cov-${{ matrix.python-version }}.xml
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
ref: ${{ github.event.release.target_commitish }}

- name: Setup Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v5.1.1
uses: actions/setup-python@v5.2.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}

Expand Down
4 changes: 4 additions & 0 deletions asusrouter/asusrouter.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ async def async_get_identity(self, force: bool = False) -> AsusDevice:
remove_data_rule(AsusData.WIREGUARD_CLIENT)
remove_data_rule(AsusData.WIREGUARD_SERVER)

# DSL connection
if self._identity.dsl is False:
remove_data_rule(AsusData.DSL)

# Ookla Speedtest
if self._identity.ookla is False:
remove_data_rule(AsusData.SPEEDTEST)
Expand Down
97 changes: 73 additions & 24 deletions asusrouter/connection.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Connection module.
This module handles the connection between the library and the router
This module handles the connection between the library and the router
as well as all the data transfer."""

from __future__ import annotations
Expand Down Expand Up @@ -29,7 +29,9 @@
_LOGGER = logging.getLogger(__name__)


def generate_credentials(username: str, password: str) -> Tuple[str, dict[str, str]]:
def generate_credentials(
username: str, password: str
) -> Tuple[str, dict[str, str]]:
"""Generate credentials for connection"""

auth = f"{username}:{password}".encode("ascii")
Expand All @@ -43,18 +45,6 @@ def generate_credentials(username: str, password: str) -> Tuple[str, dict[str, s
class Connection: # pylint: disable=too-many-instance-attributes
"""A connection between the library and the device."""

# A token for the current session
_token: Optional[str] = None
# A header to use in the current session
_header: Optional[dict[str, str]] = None

# SSL
_ssl = False

# Connection status
_connected: bool = False
_connection_lock: asyncio.Lock = asyncio.Lock()

def __init__( # pylint: disable=too-many-arguments
self,
hostname: str,
Expand All @@ -69,26 +59,71 @@ def __init__( # pylint: disable=too-many-arguments

_LOGGER.debug("Initializing a new connection to `%s`", hostname)

# Initialize parameters for connection
self._token: Optional[str] = None
self._header: Optional[dict[str, str]] = None
self._connected: bool = False
self._connection_lock: asyncio.Lock = asyncio.Lock()

# Hostname and credentials
self._hostname = hostname
self._username = username
self._password = password

# Client session
self._session = session if session is not None else self._new_session()
self._manage_session: bool = False
if session is not None:
_LOGGER.debug("Using provided session")
self._session = session
else:
_LOGGER.debug("No session provided. Will create a new one")
self._session = self._new_session()
_LOGGER.debug("Using session `%s`", session)

# Set the port and protocol based on the input
self._http = "https" if use_ssl else "http"
self._port = port or (8443 if use_ssl else 80)
_LOGGER.debug("Using `%s` and port `%s`", self._http, self._port)
self._ssl = use_ssl
self._verify_ssl = False
_LOGGER.debug(
"Using `%s` and port `%s` with ssl flag `%s`",
self._http,
self._port,
self._ssl,
)

# Callback for dumping data
self._dumpback = dumpback

def __del__(self) -> None:
"""Proper cleanup when the object is deleted."""

if self._manage_session and self._session:
if not self._session.closed:
_LOGGER.debug(
"Connection object is managing a session. Trying to close it"
)
try:
loop = asyncio.get_running_loop()
loop.create_task(self._session.close())
_LOGGER.debug("Session close task created")
except RuntimeError:
try:
asyncio.run(self._session.close())
_LOGGER.debug("Trying to close the session")
except Exception as ex: # pylint: disable=broad-except
_LOGGER.warning(
"Error while closing the session: %s", ex
)

else:
_LOGGER.debug("Session closed")

def _new_session(self) -> aiohttp.ClientSession:
"""Create a new session."""

# If we create a new session, we will manage it
self._manage_session = True
return aiohttp.ClientSession()

async def async_connect(
Expand All @@ -112,7 +147,9 @@ async def async_connect(
self._connected = False

# Generate payload and header for login request
payload, headers = generate_credentials(self._username, self._password)
payload, headers = generate_credentials(
self._username, self._password
)

# Request authotization
_LOGGER.debug("Requesting authorization")
Expand All @@ -139,7 +176,9 @@ async def async_connect(
The last error: {ex}"
) from ex
except Exception as ex: # pylint: disable=broad-except
_LOGGER.debug("Error while connecting to %s: %s", self._hostname, ex)
_LOGGER.debug(
"Error while connecting to %s: %s", self._hostname, ex
)
raise ex

# Convert the response to JSON
Expand Down Expand Up @@ -181,7 +220,9 @@ async def async_disconnect(self) -> bool:
_LOGGER.debug("Disconnected from %s", self._hostname)
return True
except AsusRouterError as ex:
_LOGGER.debug("Error while disconnecting from %s: %s", self._hostname, ex)
_LOGGER.debug(
"Error while disconnecting from %s: %s", self._hostname, ex
)
return False

# Anything else would mean error when disconnecting
Expand Down Expand Up @@ -252,11 +293,17 @@ async def async_query(

# If still not connected, raise an error
if not self._connected:
raise AsusRouterTimeoutError("Data cannot be retrieved. Connection failed")
raise AsusRouterTimeoutError(
"Data cannot be retrieved. Connection failed"
)

# Send the request
_LOGGER.debug("Sending `%s` request to `%s`", request_type, self._hostname)
return await self._send_request(endpoint, payload, headers, request_type)
_LOGGER.debug(
"Sending `%s` request to `%s`", request_type, self._hostname
)
return await self._send_request(
endpoint, payload, headers, request_type
)

async def _make_request(
self,
Expand All @@ -277,7 +324,9 @@ async def _make_request(
# Reconnect
await self.async_connect()
# Retry the request
return await self._make_request(endpoint, payload, headers, request_type)
return await self._make_request(
endpoint, payload, headers, request_type
)

# Check headers
if not headers:
Expand All @@ -300,7 +349,7 @@ async def _make_request(
url,
data=payload_to_send if request_type == RequestType.POST else None,
headers=headers,
ssl=self._ssl,
verify_ssl=self._verify_ssl,
) as response:
# Read the status code
resp_status = response.status
Expand Down
1 change: 1 addition & 0 deletions asusrouter/modules/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class AsusData(str, Enum):
CLIENTS = "clients"
CPU = "cpu"
DEVICEMAP = "devicemap"
DSL = "dsl"
FIRMWARE = "firmware"
FIRMWARE_NOTE = "firmware_note"
FLAGS = "flags"
Expand Down
8 changes: 8 additions & 0 deletions asusrouter/modules/data_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ def __init__(
for key, _, _ in [converters.safe_unpack_keys(element)]
if key != "get_wgsc_status"
],
"dsl": [
"dsllog_dataratedown",
"dsllog_datarateup",
]
}
ASUSDATA_NVRAM["aura"].extend([f"ledg_rgb{num}" for num in range(0, 8)])
ASUSDATA_NVRAM["vpnc"].extend(
Expand Down Expand Up @@ -256,6 +260,10 @@ def __init__(
method=wlan_nvram_request,
arguments=AsusRouterAttribute.WLAN_LIST,
),
AsusData.DSL: AsusDataFinder(
Endpoint.HOOK,
nvram=ASUSDATA_NVRAM["dsl"],
),
}


Expand Down
1 change: 1 addition & 0 deletions asusrouter/modules/endpoint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Endpoint(str, Enum):
"""Endpoint enum. These endpoints are used to receive data from the device."""

DEVICEMAP = "ajax_status.xml"
DSL = "ajax_AdslStatus.asp"
ETHERNET_PORTS = "ajax_ethernet_ports.asp"
FIRMWARE = "detect_firmware.asp"
FIRMWARE_NOTE = "release_note0.asp"
Expand Down
30 changes: 30 additions & 0 deletions asusrouter/modules/endpoint/hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ def process(data: dict[str, Any]) -> dict[AsusData, Any]:
or "wl3_wpa_psk" in data
):
state[AsusData.WLAN] = process_wlan(data, wlan)

# DSL
if (
"dsllog_dataratedown" in data
or "dsllog_datarateup" in data
):
state[AsusData.DSL] = process_dsl(data)

return state

Expand Down Expand Up @@ -733,3 +740,26 @@ def process_wlan(
wlan[interface.value] = info

return wlan

def process_dsl(dsl_info: dict[str, Any]) -> dict[str, Any]:
"""Process DSL data"""

def remove_units(value: Optional[str]) -> Optional[str]:
"""Remove units from the value."""

if value is None:
return None
return value.split(" ")[0]

dsl: dict[str, Any] = {}

# Data preprocessing
_dataratedown = remove_units(dsl_info.get("dsllog_dataratedown"))
_datarateup = remove_units(dsl_info.get("dsllog_datarateup"))

dsl["datarate"] = {
"down": safe_int(_dataratedown, 0),
"up": safe_int(_datarateup, 0),
}

return dsl
6 changes: 6 additions & 0 deletions asusrouter/modules/identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class AsusDevice: # pylint: disable=too-many-instance-attributes
# Flags for device features
aura: bool = False
aura_zone: int = 0
dsl: bool = False
led: bool = False
ookla: bool = False
vpn_status: bool = False
Expand Down Expand Up @@ -129,6 +130,11 @@ async def collect_identity(
if isinstance(this_device, AiMeshDevice):
identity["model"] = this_device.model

# Check if DSL
if endpoints[Endpoint.DSL] is True:
identity["dsl"] = True
_LOGGER.debug("DSL-compatible device detected")

# Check if Merlin
if endpoints[Endpoint.SYSINFO] is True:
identity["merlin"] = True
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "asusrouter"
version = "1.12.2"
version = "1.13.0"
license = {text = "Apache-2.0"}
requires-python = ">=3.11.0"
readme = "README.md"
Expand All @@ -22,7 +22,7 @@ classifiers = [
]
dependencies = [
"aiohttp >=3.8.1",
"python-dateutil ==2.8.2",
"python-dateutil ==2.9.0.post0",
"urllib3 >=1.26.14",
"xmltodict >=0.12.0",
]
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Requirements for AsusRouter

aiohttp>=3.8.1
python-dateutil==2.8.2
python-dateutil==2.9.0.post0
urllib3>=1.26.14
xmltodict>=0.12.0
Loading

0 comments on commit e2a1e0d

Please sign in to comment.