"""Implementation of the emeter module.""" from __future__ import annotations from datetime import datetime from ... import Device from ...emeterstatus import EmeterStatus from ...feature import Feature from .usage import Usage class Emeter(Usage): """Emeter module.""" def __init__(self, device: Device, module: str): super().__init__(device, module) self._add_feature( Feature( device, name="Current consumption", attribute_getter="current_consumption", container=self, unit="W", id="current_power_w", # for homeassistant backwards compat precision_hint=1, category=Feature.Category.Primary, ) ) self._add_feature( Feature( device, name="Today's consumption", attribute_getter="emeter_today", container=self, unit="kWh", id="today_energy_kwh", # for homeassistant backwards compat precision_hint=3, category=Feature.Category.Info, ) ) self._add_feature( Feature( device, id="consumption_this_month", name="This month's consumption", attribute_getter="emeter_this_month", container=self, unit="kWh", precision_hint=3, category=Feature.Category.Info, ) ) self._add_feature( Feature( device, name="Total consumption since reboot", attribute_getter="emeter_total", container=self, unit="kWh", id="total_energy_kwh", # for homeassistant backwards compat precision_hint=3, category=Feature.Category.Info, ) ) self._add_feature( Feature( device, name="Voltage", attribute_getter="voltage", container=self, unit="V", id="voltage", # for homeassistant backwards compat precision_hint=1, category=Feature.Category.Primary, ) ) self._add_feature( Feature( device, name="Current", attribute_getter="current", container=self, unit="A", id="current_a", # for homeassistant backwards compat precision_hint=2, category=Feature.Category.Primary, ) ) @property # type: ignore def realtime(self) -> EmeterStatus: """Return current energy readings.""" return EmeterStatus(self.data["get_realtime"]) @property def emeter_today(self) -> float | None: """Return today's energy consumption in kWh.""" raw_data = self.daily_data today = datetime.now().day data = self._convert_stat_data(raw_data, entry_key="day", key=today) return data.get(today) @property def emeter_this_month(self) -> float | None: """Return this month's energy consumption in kWh.""" raw_data = self.monthly_data current_month = datetime.now().month data = self._convert_stat_data(raw_data, entry_key="month", key=current_month) return data.get(current_month) @property def current_consumption(self) -> float | None: """Get the current power consumption in Watt.""" return self.realtime.power @property def emeter_total(self) -> float | None: """Return total consumption since last reboot in kWh.""" return self.realtime.total @property def current(self) -> float | None: """Return the current in A.""" return self.realtime.current @property def voltage(self) -> float | None: """Get the current voltage in V.""" return self.realtime.voltage async def erase_stats(self): """Erase all stats. Uses different query than usage meter. """ return await self.call("erase_emeter_stat") async def get_realtime(self): """Return real-time statistics.""" return await self.call("get_realtime") async def get_daystat(self, *, year=None, month=None, kwh=True) -> dict: """Return daily stats for the given year & month. The return value is a dictionary of {day: energy, ...}. """ data = await self.get_raw_daystat(year=year, month=month) data = self._convert_stat_data(data["day_list"], entry_key="day", kwh=kwh) return data async def get_monthstat(self, *, year=None, kwh=True) -> dict: """Return monthly stats for the given year. The return value is a dictionary of {month: energy, ...}. """ data = await self.get_raw_monthstat(year=year) data = self._convert_stat_data(data["month_list"], entry_key="month", kwh=kwh) return data def _convert_stat_data( self, data: list[dict[str, int | float]], entry_key: str, kwh: bool = True, key: int | None = None, ) -> dict[int | float, int | float]: """Return emeter information keyed with the day/month. The incoming data is a list of dictionaries:: [{'year': int, 'month': int, 'day': int, <-- for get_daystat not get_monthstat 'energy_wh': int, <-- for emeter in some versions (wh) 'energy': float <-- for emeter in other versions (kwh) }, ...] :return: a dictionary keyed by day or month with energy as the value. """ if not data: return {} scale: float = 1 if "energy_wh" in data[0]: value_key = "energy_wh" if kwh: scale = 1 / 1000 else: value_key = "energy" if not kwh: scale = 1000 if key is None: # Return all the data return {entry[entry_key]: entry[value_key] * scale for entry in data} # In this case we want a specific key in the data # i.e. the current day or month. # # Since we usually want the data at the end of the list so we can # optimize the search by starting at the end and avoid scaling # the data we don't need. # for entry in reversed(data): if entry[entry_key] == key: return {entry[entry_key]: entry[value_key] * scale} return {}