mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-06 10:44:04 +00:00
Initial implementation for modularized smartdevice (#757)
The initial steps to modularize the smartdevice. Modules are initialized based on the component negotiation, and each module can indicate which features it supports and which queries should be run during the update cycle.
This commit is contained in:
@@ -4,7 +4,6 @@ from .antitheft import Antitheft
|
||||
from .cloud import Cloud
|
||||
from .countdown import Countdown
|
||||
from .emeter import Emeter
|
||||
from .module import IotModule
|
||||
from .motion import Motion
|
||||
from .rulemodule import Rule, RuleModule
|
||||
from .schedule import Schedule
|
||||
@@ -17,7 +16,6 @@ __all__ = [
|
||||
"Cloud",
|
||||
"Countdown",
|
||||
"Emeter",
|
||||
"IotModule",
|
||||
"Motion",
|
||||
"Rule",
|
||||
"RuleModule",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
"""Implementation of the ambient light (LAS) module found in some dimmers."""
|
||||
from .module import IotModule
|
||||
from ..iotmodule import IotModule
|
||||
|
||||
# TODO create tests and use the config reply there
|
||||
# [{"hw_id":0,"enable":0,"dark_index":1,"min_adc":0,"max_adc":2450,
|
||||
|
@@ -5,7 +5,7 @@ except ImportError:
|
||||
from pydantic import BaseModel
|
||||
|
||||
from ...feature import Feature, FeatureType
|
||||
from .module import IotModule
|
||||
from ..iotmodule import IotModule
|
||||
|
||||
|
||||
class CloudInfo(BaseModel):
|
||||
|
@@ -1,96 +0,0 @@
|
||||
"""Base class for all module implementations."""
|
||||
import collections
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Dict
|
||||
|
||||
from ...exceptions import SmartDeviceException
|
||||
from ...feature import Feature
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from kasa.iot import IotDevice
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# TODO: This is used for query construcing
|
||||
def merge(d, u):
|
||||
"""Update dict recursively."""
|
||||
for k, v in u.items():
|
||||
if isinstance(v, collections.abc.Mapping):
|
||||
d[k] = merge(d.get(k, {}), v)
|
||||
else:
|
||||
d[k] = v
|
||||
return d
|
||||
|
||||
|
||||
class IotModule(ABC):
|
||||
"""Base class implemention for all modules.
|
||||
|
||||
The base classes should implement `query` to return the query they want to be
|
||||
executed during the regular update cycle.
|
||||
"""
|
||||
|
||||
def __init__(self, device: "IotDevice", module: str):
|
||||
self._device = device
|
||||
self._module = module
|
||||
self._module_features: Dict[str, Feature] = {}
|
||||
|
||||
def _add_feature(self, feature: Feature):
|
||||
"""Add module feature."""
|
||||
feature_name = f"{self._module}_{feature.name}"
|
||||
if feature_name in self._module_features:
|
||||
raise SmartDeviceException("Duplicate name detected %s" % feature_name)
|
||||
self._module_features[feature_name] = feature
|
||||
|
||||
@abstractmethod
|
||||
def query(self):
|
||||
"""Query to execute during the update cycle.
|
||||
|
||||
The inheriting modules implement this to include their wanted
|
||||
queries to the query that gets executed when Device.update() gets called.
|
||||
"""
|
||||
|
||||
@property
|
||||
def estimated_query_response_size(self):
|
||||
"""Estimated maximum size of query response.
|
||||
|
||||
The inheriting modules implement this to estimate how large a query response
|
||||
will be so that queries can be split should an estimated response be too large
|
||||
"""
|
||||
return 256 # Estimate for modules that don't specify
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
"""Return the module specific raw data from the last update."""
|
||||
if self._module not in self._device._last_update:
|
||||
raise SmartDeviceException(
|
||||
f"You need to call update() prior accessing module data"
|
||||
f" for '{self._module}'"
|
||||
)
|
||||
|
||||
return self._device._last_update[self._module]
|
||||
|
||||
@property
|
||||
def is_supported(self) -> bool:
|
||||
"""Return whether the module is supported by the device."""
|
||||
if self._module not in self._device._last_update:
|
||||
_LOGGER.debug("Initial update, so consider supported: %s", self._module)
|
||||
return True
|
||||
|
||||
return "err_code" not in self.data
|
||||
|
||||
def call(self, method, params=None):
|
||||
"""Call the given method with the given parameters."""
|
||||
return self._device._query_helper(self._module, method, params)
|
||||
|
||||
def query_for_command(self, query, params=None):
|
||||
"""Create a request object for the given parameters."""
|
||||
return self._device._create_request(self._module, query, params)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Module {self.__class__.__name__} ({self._module})"
|
||||
f" for {self._device.host}>"
|
||||
)
|
@@ -3,7 +3,7 @@ from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from ...exceptions import SmartDeviceException
|
||||
from .module import IotModule
|
||||
from ..iotmodule import IotModule
|
||||
|
||||
|
||||
class Range(Enum):
|
||||
|
@@ -9,7 +9,7 @@ except ImportError:
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
from .module import IotModule, merge
|
||||
from ..iotmodule import IotModule, merge
|
||||
|
||||
|
||||
class Action(Enum):
|
||||
|
@@ -2,7 +2,7 @@
|
||||
from datetime import datetime
|
||||
|
||||
from ...exceptions import SmartDeviceException
|
||||
from .module import IotModule, merge
|
||||
from ..iotmodule import IotModule, merge
|
||||
|
||||
|
||||
class Time(IotModule):
|
||||
|
@@ -2,7 +2,7 @@
|
||||
from datetime import datetime
|
||||
from typing import Dict
|
||||
|
||||
from .module import IotModule, merge
|
||||
from ..iotmodule import IotModule, merge
|
||||
|
||||
|
||||
class Usage(IotModule):
|
||||
|
Reference in New Issue
Block a user