mirror of
				https://github.com/python-kasa/python-kasa.git
				synced 2025-10-31 04:31:54 +00:00 
			
		
		
		
	Improve emeterstatus API, move into own module (#205)
Adds the following properties to EmeterStatus for saner API: * voltage (in V) * power (in W) * current (in A) * total (in kWh)
This commit is contained in:
		| @@ -14,10 +14,11 @@ to be handled by the user of the library. | ||||
| from importlib_metadata import version  # type: ignore | ||||
|  | ||||
| from kasa.discover import Discover | ||||
| from kasa.emeterstatus import EmeterStatus | ||||
| from kasa.exceptions import SmartDeviceException | ||||
| from kasa.protocol import TPLinkSmartHomeProtocol | ||||
| from kasa.smartbulb import SmartBulb | ||||
| from kasa.smartdevice import DeviceType, EmeterStatus, SmartDevice | ||||
| from kasa.smartdevice import DeviceType, SmartDevice | ||||
| from kasa.smartdimmer import SmartDimmer | ||||
| from kasa.smartlightstrip import SmartLightStrip | ||||
| from kasa.smartplug import SmartPlug | ||||
|   | ||||
							
								
								
									
										82
									
								
								kasa/emeterstatus.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								kasa/emeterstatus.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| """Module for emeter container.""" | ||||
| import logging | ||||
| from typing import Optional | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class EmeterStatus(dict): | ||||
|     """Container for converting different representations of emeter data. | ||||
|  | ||||
|     Newer FW/HW versions postfix the variable names with the used units, | ||||
|     where-as the olders do not have this feature. | ||||
|  | ||||
|     This class automatically converts between these two to allow | ||||
|     backwards and forwards compatibility. | ||||
|     """ | ||||
|  | ||||
|     @property | ||||
|     def voltage(self) -> Optional[float]: | ||||
|         """Return voltage in V.""" | ||||
|         try: | ||||
|             return self["voltage"] | ||||
|         except ValueError: | ||||
|             return None | ||||
|  | ||||
|     @property | ||||
|     def power(self) -> Optional[float]: | ||||
|         """Return power in W.""" | ||||
|         try: | ||||
|             return self["power"] | ||||
|         except ValueError: | ||||
|             return None | ||||
|  | ||||
|     @property | ||||
|     def current(self) -> Optional[float]: | ||||
|         """Return current in A.""" | ||||
|         try: | ||||
|             return self["current"] | ||||
|         except ValueError: | ||||
|             return None | ||||
|  | ||||
|     @property | ||||
|     def total(self) -> Optional[float]: | ||||
|         """Return total in kWh.""" | ||||
|         try: | ||||
|             return self["total"] | ||||
|         except ValueError: | ||||
|             return None | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return f"<EmeterStatus power={self.power} voltage={self.voltage} current={self.current} total={self.total}>" | ||||
|  | ||||
|     def __getitem__(self, item): | ||||
|         valid_keys = [ | ||||
|             "voltage_mv", | ||||
|             "power_mw", | ||||
|             "current_ma", | ||||
|             "energy_wh", | ||||
|             "total_wh", | ||||
|             "voltage", | ||||
|             "power", | ||||
|             "current", | ||||
|             "total", | ||||
|             "energy", | ||||
|         ] | ||||
|  | ||||
|         # 1. if requested data is available, return it | ||||
|         if item in super().keys(): | ||||
|             return super().__getitem__(item) | ||||
|         # otherwise decide how to convert it | ||||
|         else: | ||||
|             if item not in valid_keys: | ||||
|                 raise KeyError(item) | ||||
|             if "_" in item:  # upscale | ||||
|                 return super().__getitem__(item[: item.find("_")]) * 1000 | ||||
|             else:  # downscale | ||||
|                 for i in super().keys(): | ||||
|                     if i.startswith(item): | ||||
|                         return self.__getitem__(i) / 1000 | ||||
|  | ||||
|                 _LOGGER.debug(f"Unable to find value for '{item}'") | ||||
|                 return None | ||||
| @@ -3,12 +3,7 @@ import logging | ||||
| import re | ||||
| from typing import Any, Dict, NamedTuple, cast | ||||
|  | ||||
| from kasa.smartdevice import ( | ||||
|     DeviceType, | ||||
|     SmartDevice, | ||||
|     SmartDeviceException, | ||||
|     requires_update, | ||||
| ) | ||||
| from .smartdevice import DeviceType, SmartDevice, SmartDeviceException, requires_update | ||||
|  | ||||
|  | ||||
| class ColorTempRange(NamedTuple): | ||||
|   | ||||
| @@ -19,6 +19,7 @@ from datetime import datetime, timedelta | ||||
| from enum import Enum, auto | ||||
| from typing import Any, Dict, List, Optional | ||||
|  | ||||
| from .emeterstatus import EmeterStatus | ||||
| from .exceptions import SmartDeviceException | ||||
| from .protocol import TPLinkSmartHomeProtocol | ||||
|  | ||||
| @@ -50,48 +51,6 @@ class WifiNetwork: | ||||
|     rssi: Optional[int] = None | ||||
|  | ||||
|  | ||||
| class EmeterStatus(dict): | ||||
|     """Container for converting different representations of emeter data. | ||||
|  | ||||
|     Newer FW/HW versions postfix the variable names with the used units, | ||||
|     where-as the olders do not have this feature. | ||||
|  | ||||
|     This class automatically converts between these two to allow | ||||
|     backwards and forwards compatibility. | ||||
|     """ | ||||
|  | ||||
|     def __getitem__(self, item): | ||||
|         valid_keys = [ | ||||
|             "voltage_mv", | ||||
|             "power_mw", | ||||
|             "current_ma", | ||||
|             "energy_wh", | ||||
|             "total_wh", | ||||
|             "voltage", | ||||
|             "power", | ||||
|             "current", | ||||
|             "total", | ||||
|             "energy", | ||||
|         ] | ||||
|  | ||||
|         # 1. if requested data is available, return it | ||||
|         if item in super().keys(): | ||||
|             return super().__getitem__(item) | ||||
|         # otherwise decide how to convert it | ||||
|         else: | ||||
|             if item not in valid_keys: | ||||
|                 raise KeyError(item) | ||||
|             if "_" in item:  # upscale | ||||
|                 return super().__getitem__(item[: item.find("_")]) * 1000 | ||||
|             else:  # downscale | ||||
|                 for i in super().keys(): | ||||
|                     if i.startswith(item): | ||||
|                         return self.__getitem__(i) / 1000 | ||||
|  | ||||
|                 _LOGGER.debug(f"Unable to find value for '{item}'") | ||||
|                 return None | ||||
|  | ||||
|  | ||||
| def requires_update(f): | ||||
|     """Indicate that `update` should be called before accessing this method."""  # noqa: D202 | ||||
|     if inspect.iscoroutinefunction(f): | ||||
| @@ -202,7 +161,7 @@ class SmartDevice: | ||||
|         >>> dev.has_emeter | ||||
|         True | ||||
|         >>> dev.emeter_realtime | ||||
|         {'current': 0.015342, 'err_code': 0, 'power': 0.983971, 'total': 32.448, 'voltage': 235.595234} | ||||
|         <EmeterStatus power=0.983971 voltage=235.595234 current=0.015342 total=32.448> | ||||
|         >>> dev.emeter_today | ||||
|         >>> dev.emeter_this_month | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import pytest | ||||
|  | ||||
| from kasa import SmartDeviceException | ||||
| from kasa import EmeterStatus, SmartDeviceException | ||||
|  | ||||
| from .conftest import has_emeter, no_emeter, pytestmark | ||||
| from .newfakes import CURRENT_CONSUMPTION_SCHEMA | ||||
| @@ -121,8 +121,6 @@ async def test_current_consumption(dev): | ||||
|  | ||||
| async def test_emeterstatus_missing_current(): | ||||
|     """KL125 does not report 'current' for emeter.""" | ||||
|     from kasa import EmeterStatus | ||||
|  | ||||
|     regular = EmeterStatus( | ||||
|         {"err_code": 0, "power_mw": 0, "total_wh": 13, "current_ma": 123} | ||||
|     ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Teemu R
					Teemu R