mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-09 20:24:02 +00:00
Add consumables module for vacuums (#1327)
Some checks failed
CI / Perform linting checks (3.13) (push) Has been cancelled
CodeQL checks / Analyze (python) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Has been cancelled
Some checks failed
CI / Perform linting checks (3.13) (push) Has been cancelled
CodeQL checks / Analyze (python) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Has been cancelled
Co-authored-by: Steven B <51370195+sdb9696@users.noreply.github.com>
This commit is contained in:
@@ -14,6 +14,7 @@ from .cleanrecords import CleanRecords
|
||||
from .cloud import Cloud
|
||||
from .color import Color
|
||||
from .colortemperature import ColorTemperature
|
||||
from .consumables import Consumables
|
||||
from .contactsensor import ContactSensor
|
||||
from .devicemodule import DeviceModule
|
||||
from .dustbin import Dustbin
|
||||
@@ -76,6 +77,7 @@ __all__ = [
|
||||
"FrostProtection",
|
||||
"Thermostat",
|
||||
"Clean",
|
||||
"Consumables",
|
||||
"CleanRecords",
|
||||
"SmartLightEffect",
|
||||
"OverheatProtection",
|
||||
|
170
kasa/smart/modules/consumables.py
Normal file
170
kasa/smart/modules/consumables.py
Normal file
@@ -0,0 +1,170 @@
|
||||
"""Implementation of vacuum consumables."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
|
||||
from ...feature import Feature
|
||||
from ..smartmodule import SmartModule
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class _ConsumableMeta:
|
||||
"""Consumable meta container."""
|
||||
|
||||
#: Name of the consumable.
|
||||
name: str
|
||||
#: Internal id of the consumable
|
||||
id: str
|
||||
#: Data key in the device reported data
|
||||
data_key: str
|
||||
#: Lifetime
|
||||
lifetime: timedelta
|
||||
|
||||
|
||||
@dataclass
|
||||
class Consumable:
|
||||
"""Consumable container."""
|
||||
|
||||
#: Name of the consumable.
|
||||
name: str
|
||||
#: Id of the consumable
|
||||
id: str
|
||||
#: Lifetime
|
||||
lifetime: timedelta
|
||||
#: Used
|
||||
used: timedelta
|
||||
#: Remaining
|
||||
remaining: timedelta
|
||||
#: Device data key
|
||||
_data_key: str
|
||||
|
||||
|
||||
CONSUMABLE_METAS = [
|
||||
_ConsumableMeta(
|
||||
"Main brush",
|
||||
id="main_brush",
|
||||
data_key="roll_brush_time",
|
||||
lifetime=timedelta(hours=400),
|
||||
),
|
||||
_ConsumableMeta(
|
||||
"Side brush",
|
||||
id="side_brush",
|
||||
data_key="edge_brush_time",
|
||||
lifetime=timedelta(hours=200),
|
||||
),
|
||||
_ConsumableMeta(
|
||||
"Filter",
|
||||
id="filter",
|
||||
data_key="filter_time",
|
||||
lifetime=timedelta(hours=200),
|
||||
),
|
||||
_ConsumableMeta(
|
||||
"Sensor",
|
||||
id="sensor",
|
||||
data_key="sensor_time",
|
||||
lifetime=timedelta(hours=30),
|
||||
),
|
||||
_ConsumableMeta(
|
||||
"Charging contacts",
|
||||
id="charging_contacts",
|
||||
data_key="charge_contact_time",
|
||||
lifetime=timedelta(hours=30),
|
||||
),
|
||||
# Unknown keys: main_brush_lid_time, rag_time
|
||||
]
|
||||
|
||||
|
||||
class Consumables(SmartModule):
|
||||
"""Implementation of vacuum consumables."""
|
||||
|
||||
REQUIRED_COMPONENT = "consumables"
|
||||
QUERY_GETTER_NAME = "getConsumablesInfo"
|
||||
|
||||
_consumables: dict[str, Consumable] = {}
|
||||
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features."""
|
||||
for c_meta in CONSUMABLE_METAS:
|
||||
if c_meta.data_key not in self.data:
|
||||
continue
|
||||
|
||||
self._add_feature(
|
||||
Feature(
|
||||
self._device,
|
||||
id=f"{c_meta.id}_used",
|
||||
name=f"{c_meta.name} used",
|
||||
container=self,
|
||||
attribute_getter=lambda _, c_id=c_meta.id: self._consumables[
|
||||
c_id
|
||||
].used,
|
||||
category=Feature.Category.Debug,
|
||||
type=Feature.Type.Sensor,
|
||||
)
|
||||
)
|
||||
|
||||
self._add_feature(
|
||||
Feature(
|
||||
self._device,
|
||||
id=f"{c_meta.id}_remaining",
|
||||
name=f"{c_meta.name} remaining",
|
||||
container=self,
|
||||
attribute_getter=lambda _, c_id=c_meta.id: self._consumables[
|
||||
c_id
|
||||
].remaining,
|
||||
category=Feature.Category.Info,
|
||||
type=Feature.Type.Sensor,
|
||||
)
|
||||
)
|
||||
|
||||
self._add_feature(
|
||||
Feature(
|
||||
self._device,
|
||||
id=f"{c_meta.id}_reset",
|
||||
name=f"Reset {c_meta.name.lower()} consumable",
|
||||
container=self,
|
||||
attribute_setter=lambda c_id=c_meta.id: self.reset_consumable(c_id),
|
||||
category=Feature.Category.Debug,
|
||||
type=Feature.Type.Action,
|
||||
)
|
||||
)
|
||||
|
||||
async def _post_update_hook(self) -> None:
|
||||
"""Update the consumables."""
|
||||
if not self._consumables:
|
||||
for consumable_meta in CONSUMABLE_METAS:
|
||||
if consumable_meta.data_key not in self.data:
|
||||
continue
|
||||
used = timedelta(minutes=self.data[consumable_meta.data_key])
|
||||
consumable = Consumable(
|
||||
id=consumable_meta.id,
|
||||
name=consumable_meta.name,
|
||||
lifetime=consumable_meta.lifetime,
|
||||
used=used,
|
||||
remaining=consumable_meta.lifetime - used,
|
||||
_data_key=consumable_meta.data_key,
|
||||
)
|
||||
self._consumables[consumable_meta.id] = consumable
|
||||
else:
|
||||
for consumable in self._consumables.values():
|
||||
consumable.used = timedelta(minutes=self.data[consumable._data_key])
|
||||
consumable.remaining = consumable.lifetime - consumable.used
|
||||
|
||||
async def reset_consumable(self, consumable_id: str) -> dict:
|
||||
"""Reset consumable stats."""
|
||||
consumable_name = self._consumables[consumable_id]._data_key.removesuffix(
|
||||
"_time"
|
||||
)
|
||||
return await self.call(
|
||||
"resetConsumablesTime", {"reset_list": [consumable_name]}
|
||||
)
|
||||
|
||||
@property
|
||||
def consumables(self) -> Mapping[str, Consumable]:
|
||||
"""Get list of consumables on the device."""
|
||||
return self._consumables
|
Reference in New Issue
Block a user