mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-09 20:24:02 +00:00
Add support for cleaning records (#945)
Some checks are pending
CI / Perform linting checks (3.13) (push) Waiting to run
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Blocked by required conditions
CodeQL checks / Analyze (python) (push) Waiting to run
Some checks are pending
CI / Perform linting checks (3.13) (push) Waiting to run
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Blocked by required conditions
CodeQL checks / Analyze (python) (push) Waiting to run
Co-authored-by: Steven B. <51370195+sdb9696@users.noreply.github.com>
This commit is contained in:
61
tests/cli/test_vacuum.py
Normal file
61
tests/cli/test_vacuum.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from kasa import DeviceType, Module
|
||||
from kasa.cli.vacuum import vacuum
|
||||
|
||||
from ..device_fixtures import plug_iot
|
||||
from ..device_fixtures import vacuum as vacuum_devices
|
||||
|
||||
|
||||
@vacuum_devices
|
||||
async def test_vacuum_records_group(dev, mocker: MockerFixture, runner):
|
||||
"""Test that vacuum records calls the expected methods."""
|
||||
rec = dev.modules.get(Module.CleanRecords)
|
||||
assert rec
|
||||
|
||||
res = await runner.invoke(vacuum, ["records"], obj=dev, catch_exceptions=False)
|
||||
|
||||
latest = rec.parsed_data.last_clean
|
||||
expected = (
|
||||
f"Totals: {rec.total_clean_area} {rec.area_unit} in {rec.total_clean_time} "
|
||||
f"(cleaned {rec.total_clean_count} times)\n"
|
||||
f"Last clean: {latest.clean_area} {rec.area_unit} @ {latest.clean_time}"
|
||||
)
|
||||
assert expected in res.output
|
||||
assert res.exit_code == 0
|
||||
|
||||
|
||||
@vacuum_devices
|
||||
async def test_vacuum_records_list(dev, mocker: MockerFixture, runner):
|
||||
"""Test that vacuum records list calls the expected methods."""
|
||||
rec = dev.modules.get(Module.CleanRecords)
|
||||
assert rec
|
||||
|
||||
res = await runner.invoke(
|
||||
vacuum, ["records", "list"], obj=dev, catch_exceptions=False
|
||||
)
|
||||
|
||||
data = rec.parsed_data
|
||||
for record in data.records:
|
||||
expected = (
|
||||
f"* {record.timestamp}: cleaned {record.clean_area} {rec.area_unit}"
|
||||
f" in {record.clean_time}"
|
||||
)
|
||||
assert expected in res.output
|
||||
assert res.exit_code == 0
|
||||
|
||||
|
||||
@plug_iot
|
||||
async def test_non_vacuum(dev, mocker: MockerFixture, runner):
|
||||
"""Test that vacuum commands return an error if executed on a non-vacuum."""
|
||||
assert dev.device_type is not DeviceType.Vacuum
|
||||
|
||||
res = await runner.invoke(vacuum, ["records"], obj=dev, catch_exceptions=False)
|
||||
assert "This device does not support records" in res.output
|
||||
assert res.exit_code != 0
|
||||
|
||||
res = await runner.invoke(
|
||||
vacuum, ["records", "list"], obj=dev, catch_exceptions=False
|
||||
)
|
||||
assert "This device does not support records" in res.output
|
||||
assert res.exit_code != 0
|
@@ -180,16 +180,56 @@
|
||||
},
|
||||
"getCleanRecords": {
|
||||
"lastest_day_record": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
1736797545,
|
||||
25,
|
||||
16,
|
||||
1
|
||||
],
|
||||
"record_list": [],
|
||||
"record_list_num": 0,
|
||||
"total_area": 0,
|
||||
"total_number": 0,
|
||||
"total_time": 0
|
||||
"record_list": [
|
||||
{
|
||||
"clean_area": 17,
|
||||
"clean_time": 27,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 1,
|
||||
"map_id": 1736598799,
|
||||
"message": 1,
|
||||
"record_index": 0,
|
||||
"start_type": 1,
|
||||
"task_type": 0,
|
||||
"timestamp": 1736601522
|
||||
},
|
||||
{
|
||||
"clean_area": 14,
|
||||
"clean_time": 25,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1736598799,
|
||||
"message": 0,
|
||||
"record_index": 1,
|
||||
"start_type": 1,
|
||||
"task_type": 0,
|
||||
"timestamp": 1736684961
|
||||
},
|
||||
{
|
||||
"clean_area": 16,
|
||||
"clean_time": 25,
|
||||
"dust_collection": true,
|
||||
"error": 0,
|
||||
"info_num": 3,
|
||||
"map_id": 1736598799,
|
||||
"message": 0,
|
||||
"record_index": 2,
|
||||
"start_type": 1,
|
||||
"task_type": 0,
|
||||
"timestamp": 1736797545
|
||||
}
|
||||
],
|
||||
"record_list_num": 3,
|
||||
"total_area": 47,
|
||||
"total_number": 3,
|
||||
"total_time": 77
|
||||
},
|
||||
"getCleanStatus": {
|
||||
"getCleanStatus": {
|
||||
|
59
tests/smart/modules/test_cleanrecords.py
Normal file
59
tests/smart/modules/test_cleanrecords.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import pytest
|
||||
|
||||
from kasa import Module
|
||||
from kasa.smart import SmartDevice
|
||||
|
||||
from ...device_fixtures import get_parent_and_child_modules, parametrize
|
||||
|
||||
cleanrecords = parametrize(
|
||||
"has clean records", component_filter="clean_percent", protocol_filter={"SMART"}
|
||||
)
|
||||
|
||||
|
||||
@cleanrecords
|
||||
@pytest.mark.parametrize(
|
||||
("feature", "prop_name", "type"),
|
||||
[
|
||||
("total_clean_area", "total_clean_area", int),
|
||||
("total_clean_time", "total_clean_time", timedelta),
|
||||
("last_clean_area", "last_clean_area", int),
|
||||
("last_clean_time", "last_clean_time", timedelta),
|
||||
("total_clean_count", "total_clean_count", int),
|
||||
("last_clean_timestamp", "last_clean_timestamp", datetime),
|
||||
],
|
||||
)
|
||||
async def test_features(dev: SmartDevice, feature: str, prop_name: str, type: type):
|
||||
"""Test that features are registered and work as expected."""
|
||||
records = next(get_parent_and_child_modules(dev, Module.CleanRecords))
|
||||
assert records is not None
|
||||
|
||||
prop = getattr(records, prop_name)
|
||||
assert isinstance(prop, type)
|
||||
|
||||
feat = records._device.features[feature]
|
||||
assert feat.value == prop
|
||||
assert isinstance(feat.value, type)
|
||||
|
||||
|
||||
@cleanrecords
|
||||
async def test_timezone(dev: SmartDevice):
|
||||
"""Test that timezone is added to timestamps."""
|
||||
clean_records = next(get_parent_and_child_modules(dev, Module.CleanRecords))
|
||||
assert clean_records is not None
|
||||
|
||||
assert isinstance(clean_records.last_clean_timestamp, datetime)
|
||||
assert clean_records.last_clean_timestamp.tzinfo
|
||||
|
||||
# Check for zone info to ensure that this wasn't picking upthe default
|
||||
# of utc before the time module is updated.
|
||||
assert isinstance(clean_records.last_clean_timestamp.tzinfo, ZoneInfo)
|
||||
|
||||
for record in clean_records.parsed_data.records:
|
||||
assert isinstance(record.timestamp, datetime)
|
||||
assert record.timestamp.tzinfo
|
||||
assert isinstance(record.timestamp.tzinfo, ZoneInfo)
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
import copy
|
||||
import logging
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -100,7 +101,7 @@ async def test_initial_update(dev: SmartDevice, mocker: MockerFixture):
|
||||
# As the fixture data is already initialized, we reset the state for testing
|
||||
dev._components_raw = None
|
||||
dev._components = {}
|
||||
dev._modules = {}
|
||||
dev._modules = OrderedDict()
|
||||
dev._features = {}
|
||||
dev._children = {}
|
||||
dev._last_update = {}
|
||||
|
Reference in New Issue
Block a user