python-mytnb 0.5.1__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/PKG-INFO +1 -1
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/pyproject.toml +1 -1
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/models.py +23 -4
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/tests/test_models.py +36 -5
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/uv.lock +1 -1
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/.github/workflows/ci.yml +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/.github/workflows/publish.yml +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/.gitignore +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/LICENSE +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/README.md +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/__init__.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/__main__.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/auth.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/cli.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/client/__init__.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/client/auth.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/client/client.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/client/config.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/client/legacy.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/client/rest.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/crypto.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/src/mytnb/exceptions.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/tests/test_auth.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/tests/test_cli.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/tests/test_client.py +0 -0
- {python_mytnb-0.5.1 → python_mytnb-0.5.2}/tests/test_crypto.py +0 -0
|
@@ -269,10 +269,29 @@ class AccountUsage(BaseModel):
|
|
|
269
269
|
|
|
270
270
|
@property
|
|
271
271
|
def average_usage_kwh(self) -> Optional[float]:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
272
|
+
"""Average daily consumption (kWh) across available daily readings.
|
|
273
|
+
|
|
274
|
+
Computed from ``by_day`` rather than a usage metric: the API's
|
|
275
|
+
``AVERAGEUSAGE`` metric is a percentage comparison against the previous
|
|
276
|
+
bill period (e.g. "20% — More than the last bill period"), not a kWh
|
|
277
|
+
value. Days with no recorded consumption are excluded: both missing
|
|
278
|
+
readings and zero-usage days (such as the current, still-partial day).
|
|
279
|
+
Returns None when there is no usable daily data.
|
|
280
|
+
"""
|
|
281
|
+
values: list[float] = []
|
|
282
|
+
for week in self.by_day:
|
|
283
|
+
for day in week.days:
|
|
284
|
+
if day.is_missing_reading:
|
|
285
|
+
continue
|
|
286
|
+
try:
|
|
287
|
+
consumption = day.consumption_kwh
|
|
288
|
+
except (ValueError, TypeError):
|
|
289
|
+
continue
|
|
290
|
+
if consumption > 0:
|
|
291
|
+
values.append(consumption)
|
|
292
|
+
if not values:
|
|
293
|
+
return None
|
|
294
|
+
return round(sum(values) / len(values), 2)
|
|
276
295
|
|
|
277
296
|
@property
|
|
278
297
|
def current_cost_rm(self) -> Optional[float]:
|
|
@@ -88,12 +88,15 @@ FULL_API_RESPONSE = {
|
|
|
88
88
|
"OtherUsageMetrics": {
|
|
89
89
|
"Usage": [
|
|
90
90
|
USAGE_METRIC_DATA,
|
|
91
|
+
# Real API shape: AVERAGEUSAGE is a percentage comparison vs the last
|
|
92
|
+
# bill period, not a kWh average (that's why average_usage_kwh is
|
|
93
|
+
# computed from by_day instead of read from this metric).
|
|
91
94
|
{
|
|
92
|
-
"Key": "
|
|
95
|
+
"Key": "AVERAGEUSAGE",
|
|
93
96
|
"Title": "Avg Usage",
|
|
94
|
-
"SubTitle": "
|
|
95
|
-
"Value": "
|
|
96
|
-
"ValueUnit": "
|
|
97
|
+
"SubTitle": "More than the last bill period",
|
|
98
|
+
"Value": "20%",
|
|
99
|
+
"ValueUnit": "",
|
|
97
100
|
},
|
|
98
101
|
],
|
|
99
102
|
"Cost": [
|
|
@@ -245,8 +248,36 @@ class TestAccountUsage:
|
|
|
245
248
|
assert usage.current_usage_kwh == 234.5
|
|
246
249
|
|
|
247
250
|
def test_average_usage_kwh(self):
|
|
251
|
+
# Computed from by_day (mean of daily consumption), not a usage metric.
|
|
248
252
|
usage = AccountUsage.from_api_response(FULL_API_RESPONSE)
|
|
249
|
-
assert usage.average_usage_kwh ==
|
|
253
|
+
assert usage.average_usage_kwh == 8.5
|
|
254
|
+
|
|
255
|
+
def test_average_usage_kwh_multiple_days(self):
|
|
256
|
+
data = {
|
|
257
|
+
"ByDay": [
|
|
258
|
+
{
|
|
259
|
+
"Range": "Week 1",
|
|
260
|
+
"Days": [
|
|
261
|
+
# Two valid days that count toward the average.
|
|
262
|
+
{"Date": "2026-06-01", "Year": "2026", "Month": "06",
|
|
263
|
+
"Day": "01", "Consumption": "10", "Amount": "2.7"},
|
|
264
|
+
{"Date": "2026-06-02", "Year": "2026", "Month": "06",
|
|
265
|
+
"Day": "02", "Consumption": "20", "Amount": "5.4"},
|
|
266
|
+
# Excluded via the missing-reading flag (high value would
|
|
267
|
+
# skew the mean if the filter regressed).
|
|
268
|
+
{"Date": "2026-06-03", "Year": "2026", "Month": "06",
|
|
269
|
+
"Day": "03", "Consumption": "99", "Amount": "26.7",
|
|
270
|
+
"IsMissingReading": True},
|
|
271
|
+
# Excluded via zero consumption (the current partial day),
|
|
272
|
+
# independently of the missing-reading flag.
|
|
273
|
+
{"Date": "2026-06-04", "Year": "2026", "Month": "06",
|
|
274
|
+
"Day": "04", "Consumption": "0", "Amount": "0"},
|
|
275
|
+
],
|
|
276
|
+
}
|
|
277
|
+
],
|
|
278
|
+
}
|
|
279
|
+
usage = AccountUsage.from_api_response(data)
|
|
280
|
+
assert usage.average_usage_kwh == 15.0 # mean(10, 20)
|
|
250
281
|
|
|
251
282
|
def test_current_cost_rm(self):
|
|
252
283
|
usage = AccountUsage.from_api_response(FULL_API_RESPONSE)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|