8000 Improve usage module, consolidate API with emeter by rytilahti · Pull Request #249 · python-kasa/python-kasa · GitHub
[go: up one dir, main page]

Skip to content

Improve usage module, consolidate API with emeter #249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion kasa/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,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"])
Expand All @@ -313,6 +312,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)
Expand Down
59 changes: 54 additions & 5 deletions kasa/modules/emeter.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,69 @@
"""Implementation of the emeter module."""
from datetime import datetime
from typing import Dict, Optional

from ..emeterstatus import EmeterStatus
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
41 changes: 36 additions & 5 deletions kasa/modules/usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,53 @@ def query(self):
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):
Expand Down
41 changes: 6 additions & 35 deletions kasa/smartdevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ async def get_emeter_realtime(self) -> EmeterStatus:

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:
Expand All @@ -500,28 +501,14 @@ def _create_emeter_request(self, year: int = None, month: int = None):
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.."""
Expand Down Expand Up @@ -554,16 +541,7 @@ async def get_emeter_daily(
: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:
Expand All @@ -574,14 +552,7 @@ async def get_emeter_monthly(self, year: int = None, kwh: bool = True) -> Dict:
: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:
Expand All @@ -593,7 +564,7 @@ async def erase_emeter_stats(self) -> Dict:
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:
Expand Down
0