mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-23 03:33:35 +00:00
Improve usage module, consolidate API with emeter (#249)
* Consolidate API for both emeter&usage modules * Add new cli command 'usage' to query usage
This commit is contained in:
parent
3926f3224f
commit
e3588047fc
39
kasa/cli.py
39
kasa/cli.py
@ -316,7 +316,6 @@ async def emeter(dev: SmartDevice, year, month, erase):
|
||||
usage_data = await dev.get_emeter_daily(year=month.year, month=month.month)
|
||||
else:
|
||||
# Call with no argument outputs summary data and returns
|
||||
usage_data = {}
|
||||
emeter_status = dev.emeter_realtime
|
||||
|
||||
click.echo("Current: %s A" % emeter_status["current"])
|
||||
@ -334,6 +333,44 @@ async def emeter(dev: SmartDevice, year, month, erase):
|
||||
click.echo(f"{index}, {usage}")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@pass_dev
|
||||
@click.option("--year", type=click.DateTime(["%Y"]), default=None, required=False)
|
||||
@click.option("--month", type=click.DateTime(["%Y-%m"]), default=None, required=False)
|
||||
@click.option("--erase", is_flag=True)
|
||||
async def usage(dev: SmartDevice, year, month, erase):
|
||||
"""Query usage for historical consumption.
|
||||
|
||||
Daily and monthly data provided in CSV format.
|
||||
"""
|
||||
click.echo(click.style("== Usage ==", bold=True))
|
||||
usage = dev.modules["usage"]
|
||||
|
||||
if erase:
|
||||
click.echo("Erasing usage statistics..")
|
||||
click.echo(await usage.erase_stats())
|
||||
return
|
||||
|
||||
if year:
|
||||
click.echo(f"== For year {year.year} ==")
|
||||
click.echo("Month, usage (minutes)")
|
||||
usage_data = await usage.get_monthstat(year.year)
|
||||
elif month:
|
||||
click.echo(f"== For month {month.month} of {month.year} ==")
|
||||
click.echo("Day, usage (minutes)")
|
||||
usage_data = await usage.get_daystat(year=month.year, month=month.month)
|
||||
else:
|
||||
# Call with no argument outputs summary data and returns
|
||||
click.echo("Today: %s minutes" % usage.usage_today)
|
||||
click.echo("This month: %s minutes" % usage.usage_this_month)
|
||||
|
||||
return
|
||||
|
||||
# output any detailed usage data
|
||||
for index, usage in usage_data.items():
|
||||
click.echo(f"{index}, {usage}")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("brightness", type=click.IntRange(0, 100), default=None, required=False)
|
||||
@click.option("--transition", type=int, required=False)
|
||||
|
@ -1,4 +1,7 @@
|
||||
"""Implementation of the emeter module."""
|
||||
from datetime import datetime
|
||||
from typing import Dict, Optional
|
||||
|
||||
from ..emeterstatus import EmeterStatus
|
||||
from .usage import Usage
|
||||
|
||||
@ -6,15 +9,61 @@ from .usage import Usage
|
||||
class Emeter(Usage):
|
||||
"""Emeter module."""
|
||||
|
||||
def query(self):
|
||||
"""Prepare query for emeter data."""
|
||||
return self._device._create_emeter_request()
|
||||
|
||||
@property # type: ignore
|
||||
def realtime(self) -> EmeterStatus:
|
||||
"""Return current energy readings."""
|
||||
return EmeterStatus(self.data["get_realtime"])
|
||||
|
||||
@property
|
||||
def emeter_today(self) -> Optional[float]:
|
||||
"""Return today's energy consumption in kWh."""
|
||||
raw_data = self.daily_data
|
||||
today = datetime.now().day
|
||||
data = self._emeter_convert_emeter_data(raw_data)
|
||||
|
||||
return data.get(today)
|
||||
|
||||
@property
|
||||
def emeter_this_month(self) -> Optional[float]:
|
||||
"""Return this month's energy consumption in kWh."""
|
||||
raw_data = self.monthly_data
|
||||
current_month = datetime.now().month
|
||||
data = self._emeter_convert_emeter_data(raw_data)
|
||||
|
||||
return data.get(current_month)
|
||||
|
||||
async def erase_stats(self):
|
||||
"""Erase all stats."""
|
||||
"""Erase all stats.
|
||||
|
||||
Uses different query than usage meter.
|
||||
"""
|
||||
return await self.call("erase_emeter_stat")
|
||||
|
||||
async def get_daystat(self, *, year, month, kwh=True):
|
||||
"""Return daily stats for the given year & month."""
|
||||
raw_data = await super().get_daystat(year=year, month=month)
|
||||
return self._emeter_convert_emeter_data(raw_data["day_list"], kwh)
|
||||
|
||||
async def get_monthstat(self, *, year, kwh=True):
|
||||
"""Return monthly stats for the given year."""
|
||||
raw_data = await super().get_monthstat(year=year)
|
||||
return self._emeter_convert_emeter_data(raw_data["month_list"], kwh)
|
||||
|
||||
def _emeter_convert_emeter_data(self, data, kwh=True) -> Dict:
|
||||
"""Return emeter information keyed with the day/month.."""
|
||||
response = [EmeterStatus(**x) for x in data]
|
||||
|
||||
if not response:
|
||||
return {}
|
||||
|
||||
energy_key = "energy_wh"
|
||||
if kwh:
|
||||
energy_key = "energy"
|
||||
|
||||
entry_key = "month"
|
||||
if "day" in response[0]:
|
||||
entry_key = "day"
|
||||
|
||||
data = {entry[entry_key]: entry[energy_key] for entry in response}
|
||||
|
||||
return data
|
||||
|
@ -17,22 +17,53 @@ class Usage(Module):
|
||||
req, self.query_for_command("get_daystat", {"year": year, "month": month})
|
||||
)
|
||||
req = merge(req, self.query_for_command("get_monthstat", {"year": year}))
|
||||
req = merge(req, self.query_for_command("get_next_action"))
|
||||
|
||||
return req
|
||||
|
||||
async def get_daystat(self, year, month):
|
||||
"""Return stats for the current day."""
|
||||
@property
|
||||
def daily_data(self):
|
||||
"""Return statistics on daily basis."""
|
||||
return self.data["get_daystat"]["day_list"]
|
||||
|
||||
@property
|
||||
def monthly_data(self):
|
||||
"""Return statistics on monthly basis."""
|
||||
return self.data["get_monthstat"]["month_list"]
|
||||
|
||||
@property
|
||||
def usage_today(self):
|
||||
"""Return today's usage in minutes."""
|
||||
today = datetime.now().day
|
||||
converted = [x["time"] for x in self.daily_data if x["day"] == today]
|
||||
if not converted:
|
||||
return None
|
||||
|
||||
return converted.pop()
|
||||
|
||||
@property
|
||||
def usage_this_month(self):
|
||||
"""Return usage in this month in minutes."""
|
||||
this_month = datetime.now().month
|
||||
converted = [x["time"] for x in self.monthly_data if x["month"] == this_month]
|
||||
if not converted:
|
||||
return None
|
||||
|
||||
return converted.pop()
|
||||
|
||||
async def get_daystat(self, *, year=None, month=None):
|
||||
"""Return daily stats for the given year & month."""
|
||||
if year is None:
|
||||
year = datetime.now().year
|
||||
if month is None:
|
||||
month = datetime.now().month
|
||||
|
||||
return await self.call("get_daystat", {"year": year, "month": month})
|
||||
|
||||
async def get_monthstat(self, year):
|
||||
"""Return stats for the current month."""
|
||||
async def get_monthstat(self, *, year=None):
|
||||
"""Return monthly stats for the given year."""
|
||||
if year is None:
|
||||
year = datetime.now().year
|
||||
|
||||
return await self.call("get_monthstat", {"year": year})
|
||||
|
||||
async def erase_stats(self):
|
||||
|
@ -482,6 +482,7 @@ class SmartDevice:
|
||||
|
||||
def _create_emeter_request(self, year: int = None, month: int = None):
|
||||
"""Create a Internal method for building a request for all emeter statistics at once."""
|
||||
# TODO: this is currently only here for smartstrip plug support, move it there?
|
||||
if year is None:
|
||||
year = datetime.now().year
|
||||
if month is None:
|
||||
@ -506,28 +507,14 @@ class SmartDevice:
|
||||
def emeter_today(self) -> Optional[float]:
|
||||
"""Return today's energy consumption in kWh."""
|
||||
self._verify_emeter()
|
||||
raw_data = self._last_update[self.emeter_type]["get_daystat"]["day_list"]
|
||||
data = self._emeter_convert_emeter_data(raw_data)
|
||||
today = datetime.now().day
|
||||
|
||||
if today in data:
|
||||
return data[today]
|
||||
|
||||
return None
|
||||
return self.modules["emeter"].emeter_today
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def emeter_this_month(self) -> Optional[float]:
|
||||
"""Return this month's energy consumption in kWh."""
|
||||
self._verify_emeter()
|
||||
raw_data = self._last_update[self.emeter_type]["get_monthstat"]["month_list"]
|
||||
data = self._emeter_convert_emeter_data(raw_data)
|
||||
current_month = datetime.now().month
|
||||
|
||||
if current_month in data:
|
||||
return data[current_month]
|
||||
|
||||
return None
|
||||
return self.modules["emeter"].emeter_this_month
|
||||
|
||||
def _emeter_convert_emeter_data(self, data, kwh=True) -> Dict:
|
||||
"""Return emeter information keyed with the day/month.."""
|
||||
@ -560,16 +547,7 @@ class SmartDevice:
|
||||
:return: mapping of day of month to value
|
||||
"""
|
||||
self._verify_emeter()
|
||||
if year is None:
|
||||
year = datetime.now().year
|
||||
if month is None:
|
||||
month = datetime.now().month
|
||||
|
||||
response = await self._query_helper(
|
||||
self.emeter_type, "get_daystat", {"month": month, "year": year}
|
||||
)
|
||||
|
||||
return self._emeter_convert_emeter_data(response["day_list"], kwh)
|
||||
return await self.modules["emeter"].get_daystat(year=year, month=month, kwh=kwh)
|
||||
|
||||
@requires_update
|
||||
async def get_emeter_monthly(self, year: int = None, kwh: bool = True) -> Dict:
|
||||
@ -580,14 +558,7 @@ class SmartDevice:
|
||||
:return: dict: mapping of month to value
|
||||
"""
|
||||
self._verify_emeter()
|
||||
if year is None:
|
||||
year = datetime.now().year
|
||||
|
||||
response = await self._query_helper(
|
||||
self.emeter_type, "get_monthstat", {"year": year}
|
||||
)
|
||||
|
||||
return self._emeter_convert_emeter_data(response["month_list"], kwh)
|
||||
return await self.modules["emeter"].get_monthstat(year=year, kwh=kwh)
|
||||
|
||||
@requires_update
|
||||
async def erase_emeter_stats(self) -> Dict:
|
||||
@ -599,7 +570,7 @@ class SmartDevice:
|
||||
async def current_consumption(self) -> float:
|
||||
"""Get the current power consumption in Watt."""
|
||||
self._verify_emeter()
|
||||
response = EmeterStatus(await self.get_emeter_realtime())
|
||||
response = self.emeter_realtime
|
||||
return float(response["power"])
|
||||
|
||||
async def reboot(self, delay: int = 1) -> None:
|
||||
|
Loading…
Reference in New Issue
Block a user