SMART energy module stubs get_daily_stats/get_monthly_stats even when the firmware supports get_energy_data
Summary
The SMART energy module unconditionally raises KasaException("Device does not support periodic statistics") for both get_daily_stats and get_monthly_stats, for every SMART/KLAP device — regardless of whether the device firmware actually supports historical energy queries.
Many SMART metering devices (e.g. KP125M, P110/P115, etc.) implement a get_energy_data method that returns per-day and per-month history. Because the module stubs the interface methods out, none of that history is reachable through python-kasa, even though the equivalent IOT module (kasa/iot/modules/emeter.py) does implement get_daily_stats/get_monthly_stats. The result is that the library can only report current month/day totals (get_energy_usage) for SMART meters, while the Kasa mobile app shows the full history from the same device.
Where
|
async def get_daily_stats( |
|
self, *, year: int | None = None, month: int | None = None, kwh: bool = True |
|
) -> dict: |
|
"""Return daily stats for the given year & month. |
|
|
|
The return value is a dictionary of {day: energy, ...}. |
|
""" |
|
raise KasaException("Device does not support periodic statistics") |
|
|
|
async def get_monthly_stats( |
|
self, *, year: int | None = None, kwh: bool = True |
|
) -> dict: |
|
"""Return monthly stats for the given year.""" |
|
raise KasaException("Device does not support periodic statistics") |
async def get_daily_stats(self, *, year=None, month=None, kwh=True) -> dict:
"""Return daily stats for the given year & month."""
raise KasaException("Device does not support periodic statistics")
async def get_monthly_stats(self, *, year=None, kwh=True) -> dict:
"""Return monthly stats for the given year."""
raise KasaException("Device does not support periodic statistics")
Affected / tested device
- Model: KP125M(US)
- Type:
SMART.KASAPLUG, encryption KLAP, login v2
- Energy component:
{'id': 'energy_monitoring', 'ver_code': 2}
- python-kasa: 0.10.2 (stub also present on
master @ a29d061)
Reproduction
energy = dev.modules[Module.Energy]
await energy.get_monthly_stats(year=2026)
# kasa.exceptions.KasaException: Device does not support periodic statistics
…yet the firmware answers get_energy_data directly. A raw query returns real history:
resp = await dev.protocol.query({
"get_energy_data": {
"start_timestamp": <local Jan 1 00:00 of the year>,
"end_timestamp": <local Jan 1 00:00 of the next year>,
"interval": 43200, # minutes; 43200 = monthly, 1440 = daily, 60 = hourly
}
})
# -> {"get_energy_data": {
# "start_timestamp": ..., "end_timestamp": ..., "interval": 43200, "local_time": ...,
# "data": [<Wh for Jan>, <Wh for Feb>, ... 12 buckets ...]
# }}
Behaviour notes discovered while testing (relevant to any implementation)
These are firmware constraints on get_energy_data, not python-kasa bugs, but an implementation has to respect them:
- The request must be period-aligned, and the interval selects the bucket size:
interval=43200 (monthly): start_timestamp = local Jan 1 of a year → returns that year's 12 monthly buckets.
interval=1440 (daily): start_timestamp = first day of a quarter → returns that quarter's daily buckets.
interval=60 (hourly): start_timestamp = midnight of a day → returns 24 hourly buckets.
A too-large / misaligned window returns PARAMS_ERROR(-1008).
- Responses can paginate —
response.end_timestamp may be earlier than the requested end_timestamp; the app re-issues the request with that value as the new start_timestamp and concatenates the data arrays.
- Retention differs by resolution: monthly totals are kept for the whole year, but daily granularity only covers a rolling recent window (~3 months on the tested device). Older daily buckets return
0, while the monthly total for that same month is still populated. An implementation should not treat trailing/older zeros as "no data".
- Values are in Wh (consistent with
get_energy_usage.month_energy).
Proposed approach
- Implement
get_daily_stats/get_monthly_stats in kasa/smart/modules/energy.py on top of get_energy_data, mapping the returned data arrays into the interface's {day: energy} / {month: energy} dict contract and honouring the kwh flag.
- Gate on capability (component
ver_code, and/or a graceful PARAMS_ERROR/unknown-method fallback) so devices that genuinely lack get_energy_data keep raising the existing exception.
- Encode the alignment + pagination rules above.
Contribution
Happy to open a PR. I have a working KP125M against real hardware and can supply a fixture with representative get_energy_data responses (monthly + daily) plus component_nego / get_energy_usage. Guidance welcome on how you'd prefer to fixture a parameterized call like get_energy_data in the current SMART test framework, since the existing fixtures capture static responses.
SMART energy module stubs
get_daily_stats/get_monthly_statseven when the firmware supportsget_energy_dataSummary
The SMART energy module unconditionally raises
KasaException("Device does not support periodic statistics")for bothget_daily_statsandget_monthly_stats, for every SMART/KLAP device — regardless of whether the device firmware actually supports historical energy queries.Many SMART metering devices (e.g. KP125M, P110/P115, etc.) implement a
get_energy_datamethod that returns per-day and per-month history. Because the module stubs the interface methods out, none of that history is reachable through python-kasa, even though the equivalent IOT module (kasa/iot/modules/emeter.py) does implementget_daily_stats/get_monthly_stats. The result is that the library can only report current month/day totals (get_energy_usage) for SMART meters, while the Kasa mobile app shows the full history from the same device.Where
python-kasa/kasa/smart/modules/energy.py
Lines 202 to 215 in a29d061
Affected / tested device
SMART.KASAPLUG, encryptionKLAP, login v2{'id': 'energy_monitoring', 'ver_code': 2}master@a29d061)Reproduction
…yet the firmware answers
get_energy_datadirectly. A raw query returns real history:Behaviour notes discovered while testing (relevant to any implementation)
These are firmware constraints on
get_energy_data, not python-kasa bugs, but an implementation has to respect them:interval=43200(monthly):start_timestamp= local Jan 1 of a year → returns that year's 12 monthly buckets.interval=1440(daily):start_timestamp= first day of a quarter → returns that quarter's daily buckets.interval=60(hourly):start_timestamp= midnight of a day → returns 24 hourly buckets.A too-large / misaligned window returns
PARAMS_ERROR(-1008).response.end_timestampmay be earlier than the requestedend_timestamp; the app re-issues the request with that value as the newstart_timestampand concatenates thedataarrays.0, while the monthly total for that same month is still populated. An implementation should not treat trailing/older zeros as "no data".get_energy_usage.month_energy).Proposed approach
get_daily_stats/get_monthly_statsinkasa/smart/modules/energy.pyon top ofget_energy_data, mapping the returneddataarrays into the interface's{day: energy}/{month: energy}dict contract and honouring thekwhflag.ver_code, and/or a gracefulPARAMS_ERROR/unknown-method fallback) so devices that genuinely lackget_energy_datakeep raising the existing exception.Contribution
Happy to open a PR. I have a working KP125M against real hardware and can supply a fixture with representative
get_energy_dataresponses (monthly + daily) pluscomponent_nego/get_energy_usage. Guidance welcome on how you'd prefer to fixture a parameterized call likeget_energy_datain the current SMART test framework, since the existing fixtures capture static responses.