-
-
Notifications
You must be signed in to change notification settings - Fork 187
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
21 changed files
with
408 additions
and
156 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
"""Base class for all module implementations.""" | ||
import logging | ||
from abc import ABC, abstractmethod | ||
from typing import Dict | ||
|
||
from .device import Device | ||
from .exceptions import SmartDeviceException | ||
from .feature import Feature | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
class Module(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: "Device", module: str): | ||
self._device = device | ||
self._module = module | ||
self._module_features: Dict[str, 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 | ||
@abstractmethod | ||
def data(self): | ||
"""Return the module specific raw data from the last update.""" | ||
|
||
def _add_feature(self, feature: Feature): | ||
"""Add module feature.""" | ||
feat_name = f"{self._module}_{feature.name}" | ||
if feat_name in self._module_features: | ||
raise SmartDeviceException("Duplicate name detected %s" % feat_name) | ||
self._module_features[feat_name] = feature | ||
|
||
def __repr__(self) -> str: | ||
return ( | ||
f"<Module {self.__class__.__name__} ({self._module})" | ||
f" for {self._device.host}>" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
"""Modules for SMART devices.""" | ||
from .childdevicemodule import ChildDeviceModule | ||
from .devicemodule import DeviceModule | ||
from .energymodule import EnergyModule | ||
from .timemodule import TimeModule | ||
|
||
__all__ = ["TimeModule", "EnergyModule", "DeviceModule", "ChildDeviceModule"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
"""Implementation for child devices.""" | ||
from ..smartmodule import SmartModule | ||
|
||
|
||
class ChildDeviceModule(SmartModule): | ||
"""Implementation for child devices.""" | ||
|
||
REQUIRED_COMPONENT = "child_device" | ||
QUERY_GETTER_NAME = "get_child_device_list" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
"""Implementation of device module.""" | ||
from typing import Dict | ||
|
||
from ..smartmodule import SmartModule | ||
|
||
|
||
class DeviceModule(SmartModule): | ||
"""Implementation of device module.""" | ||
|
||
REQUIRED_COMPONENT = "device" | ||
|
||
def query(self) -> Dict: | ||
"""Query to execute during the update cycle.""" | ||
query = { | ||
"get_device_info": None, | ||
} | ||
# Device usage is not available on older firmware versions | ||
if self._device._components[self.REQUIRED_COMPONENT] >= 2: | ||
query["get_device_usage"] = None | ||
|
||
return query |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
"""Implementation of energy monitoring module.""" | ||
from typing import TYPE_CHECKING, Dict, Optional | ||
|
||
from ...emeterstatus import EmeterStatus | ||
from ...feature import Feature | ||
from ..smartmodule import SmartModule | ||
|
||
if TYPE_CHECKING: | ||
from ..smartdevice import SmartDevice | ||
|
||
|
||
class EnergyModule(SmartModule): | ||
"""Implementation of energy monitoring module.""" | ||
|
||
REQUIRED_COMPONENT = "energy_monitoring" | ||
|
||
def __init__(self, device: "SmartDevice", module: str): | ||
super().__init__(device, module) | ||
self._add_feature( | ||
Feature( | ||
device, | ||
name="Current consumption", | ||
attribute_getter="current_power", | ||
container=self, | ||
) | ||
) # W or mW? | ||
self._add_feature( | ||
Feature( | ||
device, | ||
name="Today's consumption", | ||
attribute_getter="emeter_today", | ||
container=self, | ||
) | ||
) # Wh or kWh? | ||
self._add_feature( | ||
Feature( | ||
device, | ||
name="This month's consumption", | ||
attribute_getter="emeter_this_month", | ||
container=self, | ||
) | ||
) # Wh or kWH? | ||
|
||
def query(self) -> Dict: | ||
"""Query to execute during the update cycle.""" | ||
return { | ||
"get_energy_usage": None, | ||
# The current_power in get_energy_usage is more precise (mw vs. w), | ||
# making this rather useless, but maybe there are version differences? | ||
"get_current_power": None, | ||
} | ||
|
||
@property | ||
def current_power(self): | ||
"""Current power.""" | ||
return self.emeter_realtime.power | ||
|
||
@property | ||
def energy(self): | ||
"""Return get_energy_usage results.""" | ||
return self.data["get_energy_usage"] | ||
|
||
@property | ||
def emeter_realtime(self): | ||
"""Get the emeter status.""" | ||
# TODO: Perhaps we should get rid of emeterstatus altogether for smartdevices | ||
return EmeterStatus( | ||
{ | ||
"power_mw": self.energy.get("current_power"), | ||
"total": self._convert_energy_data( | ||
self.energy.get("today_energy"), 1 / 1000 | ||
), | ||
} | ||
) | ||
|
||
@property | ||
def emeter_this_month(self) -> Optional[float]: | ||
"""Get the emeter value for this month.""" | ||
return self._convert_energy_data(self.energy.get("month_energy"), 1 / 1000) | ||
|
||
@property | ||
def emeter_today(self) -> Optional[float]: | ||
"""Get the emeter value for today.""" | ||
return self._convert_energy_data(self.energy.get("today_energy"), 1 / 1000) | ||
|
||
def _convert_energy_data(self, data, scale) -> Optional[float]: | ||
"""Return adjusted emeter information.""" | ||
return data if not data else data * scale |
Oops, something went wrong.