Skip to content

Commit

Permalink
1.2.0 (#400)
Browse files Browse the repository at this point in the history
* Add tests for service module (#384)

* Add `update_clients` service (#383)

* Update GitHub tests workflow (#385)

* Add `force_clients` possibility (#386)

* Add system services (#391)

* Add timestamp converters (#392)

* Add speedtest data collection (#393)

* Add Python 3.12 to the CI workflow (#398)

* Improve error messages (#399)

* Bump version to `1.2.0`

* Convert speedtest data to bps (#394)
  • Loading branch information
Vaskivskyi committed Nov 23, 2023
1 parent 179e924 commit 192d876
Show file tree
Hide file tree
Showing 16 changed files with 536 additions and 128 deletions.
127 changes: 116 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,81 @@ on:
- dev
pull_request: ~
schedule:
- cron: '0 0 * * *'

env:
DEFAULT_PYTHON: 3.11
- cron: "0 0 * * *"

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
test:
prepare:
name: Preparation
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.11, 3.12]
outputs:
pip_cache_dir: ${{ steps.pip-cache.outputs.dir }}
steps:
- name: Checkout
uses: actions/checkout@v4.1.1

- name: Set up Python ${{ env.DEFAULT_PYTHON }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4.7.1
with:
python-version: ${{ env.DEFAULT_PYTHON }}
python-version: ${{ matrix.python-version }}

- name: Get pip cache directory path
id: pip-cache
run: echo "PIP_CACHE_DIR=$(pip cache dir)" >> $GITHUB_ENV
run: echo "dir=$(pip cache dir)" >> $GITHUB_ENV

- name: Cache pip dependencies
uses: actions/cache@v3.3.2
with:
path: ${{ env.PIP_CACHE_DIR }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }}
path: ${{ env.dir }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt', '**/requirements_test.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install project dependencies
run: |
pip install .
pip install -r requirements.txt
- name: Install test dependencies
run: |
pip install -r requirements_test.txt
unit-tests:
name: Unit Tests
needs: prepare
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.11, 3.12]
steps:
- name: Checkout
uses: actions/checkout@v4.1.1

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4.7.1
with:
python-version: ${{ matrix.python-version }}

- name: Get pip cache directory path
id: pip-cache
run: echo "dir=$(pip cache dir)" >> $GITHUB_ENV

- name: Cache pip dependencies
uses: actions/cache@v3.3.2
with:
path: ${{ env.dir }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt', '**/requirements_test.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install project dependencies
run: |
pip install -r requirements.txt
- name: Install test dependencies
run: |
Expand All @@ -52,10 +91,76 @@ jobs:
run: |
pytest --cov=asusrouter --cov-report=xml:unit-tests-cov.xml -k 'not test_devices'
- name: Upload coverage to artifacts
uses: actions/upload-artifact@v3.1.3
with:
name: unit-tests-cov
path: unit-tests-cov.xml

device-tests:
name: Device Tests
needs: prepare
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.11, 3.12]
steps:
- name: Checkout
uses: actions/checkout@v4.1.1

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4.7.1
with:
python-version: ${{ matrix.python-version }}

- name: Get pip cache directory path
id: pip-cache
run: echo "dir=$(pip cache dir)" >> $GITHUB_ENV

- name: Cache pip dependencies
uses: actions/cache@v3.3.2
with:
path: ${{ env.dir }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt', '**/requirements_test.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install project dependencies
run: |
pip install -r requirements.txt
- name: Install test dependencies
run: |
pip install -r requirements_test.txt
- name: Run real-data tests
run: |
pytest --cov=asusrouter --cov-report=xml:real-data-tests-cov.xml tests/test_devices.py --log-cli-level=INFO
- name: Upload coverage to artifacts
uses: actions/upload-artifact@v3.1.3
with:
name: real-data-tests-cov
path: real-data-tests-cov.xml

codecov:
name: Codecov
needs: [unit-tests, device-tests]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.1.1

- name: Download coverage from artifacts
uses: actions/download-artifact@v3.0.2
with:
name: unit-tests-cov
path: .
- uses: actions/download-artifact@v3.0.2
with:
name: real-data-tests-cov
path: .

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3.1.4
with:
Expand Down
54 changes: 52 additions & 2 deletions asusrouter/asusrouter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
import aiohttp

from asusrouter.connection import Connection
from asusrouter.const import DEFAULT_CACHE_TIME, DEFAULT_TIMEOUT
from asusrouter.const import (
DEFAULT_CACHE_TIME,
DEFAULT_CLIENTS_WAITTIME,
DEFAULT_TIMEOUT,
)
from asusrouter.error import (
AsusRouter404Error,
AsusRouterAccessError,
Expand Down Expand Up @@ -54,6 +58,7 @@
save_state,
set_state,
)
from asusrouter.modules.system import AsusSystem
from asusrouter.tools import legacy
from asusrouter.tools.converters import safe_list
from asusrouter.tools.readers import merge_dicts
Expand All @@ -77,6 +82,10 @@ class AsusRouter:
# ID from the last called service
_last_id: Optional[int] = None

# Force clients
_force_clients: bool = False
_force_clients_waittime: float = DEFAULT_CLIENTS_WAITTIME

def __init__(
self,
hostname: str,
Expand Down Expand Up @@ -258,6 +267,13 @@ async def async_get_identity(self, force: bool = False) -> AsusDevice:
remove_data_rule(AsusData.WIREGUARD_CLIENT)
remove_data_rule(AsusData.WIREGUARD_SERVER)

# Ookla Speedtest
if self._identity.ookla is False:
remove_data_rule(AsusData.SPEEDTEST)
# remove_data_rule(AsusData.SPEEDTEST_HISTORY)
remove_data_rule(AsusData.SPEEDTEST_RESULT)
# remove_data_rule(AsusData.SPEEDTEST_SERVERS)

# Return new identity
return self._identity

Expand Down Expand Up @@ -478,6 +494,20 @@ def _drop_data(self, datatype: AsusData, endpoint: Endpoint) -> bool:

return False

async def _check_prerequisites(self, datatype: AsusData) -> None:
"""Check prerequisites before fetching data."""

# Allow forcing clients update if set by the user
if datatype == AsusData.CLIENTS:
if self._force_clients:
_LOGGER.debug("Forcing clients update")
await self.async_set_state(AsusSystem.UPDATE_CLIENTS)
_LOGGER.debug(
"Waiting for clients to be updated: %s s",
self._force_clients_waittime,
)
await asyncio.sleep(self._force_clients_waittime)

def _check_state(self, datatype: Optional[AsusData]) -> None:
"""Make sure the state object is available."""

Expand Down Expand Up @@ -528,6 +558,9 @@ async def async_get_data(self, datatype: AsusData, force: bool = False) -> Any:
# Mark the data as active
self._state[datatype].start()

# Check prerequisites
await self._check_prerequisites(datatype)

# Get the data finder
data_finder = self._where_to_get_data(datatype)

Expand Down Expand Up @@ -615,7 +648,7 @@ async def async_get_data(self, datatype: AsusData, force: bool = False) -> Any:

async def async_run_service(
self,
service: str,
service: Optional[str],
arguments: Optional[dict[str, Any]] = None,
apply: bool = False,
expect_modify: bool = True,
Expand Down Expand Up @@ -877,6 +910,23 @@ def connected(self) -> bool:
# <-- Properties
# ---------------------------

# ---------------------------
# Additional settings -->
# ---------------------------

def set_force_clients(
self, value: bool = True, waittime: Optional[float] = None
) -> None:
"""Set force clients."""

self._force_clients = value
if waittime:
self._force_clients_waittime = waittime

# ---------------------------
# <-- Additional settings
# ---------------------------

# ---------------------------
# General management -->
# ---------------------------
Expand Down
12 changes: 9 additions & 3 deletions asusrouter/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ async def async_connect(
)
_LOGGER.debug("Received authorization response")
except AsusRouterAccessError as ex:
raise ex
raise AsusRouterAccessError(
f"Cannot access {EndpointService.LOGIN}. Failed in `async_connect`"
) from ex
except AsusRouterError as ex:
_LOGGER.debug("Connection failed with error: %s", ex)
if retry < len(DEFAULT_TIMEOUTS) - 1:
Expand Down Expand Up @@ -215,9 +217,13 @@ async def _send_request(
return (resp_status, resp_headers, resp_content)
except aiohttp.ClientConnectorError as ex:
self.reset_connection()
raise AsusRouterConnectionError from ex
raise AsusRouterConnectionError(
f"Cannot connect to {self._hostname}. Failed in `_send_request`"
) from ex
except (aiohttp.ClientConnectionError, aiohttp.ClientOSError) as ex:
raise AsusRouterConnectionError from ex
raise AsusRouterConnectionError(
f"Cannot connect to {self._hostname}. Failed in `_send_request`"
) from ex
except Exception as ex: # pylint: disable=broad-except
raise ex

Expand Down
1 change: 1 addition & 0 deletions asusrouter/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class ContentType(str, Enum):

# Library defaults
DEFAULT_CACHE_TIME = 5.0
DEFAULT_CLIENTS_WAITTIME = 5.0
DEFAULT_SLEEP_TIME = 0.1
DEFAULT_TIMEOUT = 10.0
DEFAULT_TIMEOUT_CONNECTION = 180.0
Expand Down
Loading

0 comments on commit 192d876

Please sign in to comment.