tariffkit 0.2.3__tar.gz → 0.4.0__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.
- tariffkit-0.4.0/CHANGELOG.md +486 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/PKG-INFO +29 -25
- {tariffkit-0.2.3 → tariffkit-0.4.0}/README.md +28 -24
- {tariffkit-0.2.3 → tariffkit-0.4.0}/pyproject.toml +1 -1
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/__init__.py +2 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/account/cli.py +15 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/account/model.py +92 -24
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/billing/__init__.py +8 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/billing/engine.py +26 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/billing/ledger.py +63 -1
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/billing/models.py +85 -7
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/billing/netting.py +61 -5
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/billing/trueup.py +140 -14
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/cca.py +5 -0
- tariffkit-0.4.0/src/tariffkit/components.py +200 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/config.py +62 -0
- tariffkit-0.4.0/src/tariffkit/data/tariff/pge/e1/2026-03-01.toml +224 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/etouc/2025-01-01.toml +5 -1
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/etouc/2025-03-01.toml +5 -1
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/etouc/2025-09-01.toml +5 -1
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/etouc/2026-01-01.toml +5 -1
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/etouc/2026-03-01.toml +5 -1
- tariffkit-0.4.0/src/tariffkit/data/tariff/pge/etoud/2026-03-01.toml +143 -0
- tariffkit-0.4.0/src/tariffkit/data/tariff/pge/program/dcare/2025-01-01.toml +14 -0
- tariffkit-0.4.0/src/tariffkit/data/tariff/pge/program/dmedical/2026-03-01.toml +14 -0
- tariffkit-0.4.0/src/tariffkit/data/tariff/pge/program/ersmart/2022-05-01.toml +19 -0
- tariffkit-0.4.0/src/tariffkit/data/tariff/pge/program/medicalbaseline/2023-03-23.toml +13 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/engine.py +10 -1
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/interop/predbat.py +17 -11
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/models.py +20 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/mqtt/discovery.py +53 -1
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/mqtt/publisher.py +40 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/providers/pge/reconcile.py +39 -21
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/providers/pge/statements/parse.py +3 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/tariff/retail.py +84 -31
- tariffkit-0.4.0/tests/conftest.py +61 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_account.py +142 -2
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_account_cli.py +20 -1
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_billing.py +144 -0
- tariffkit-0.4.0/tests/test_components.py +164 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_eelec.py +9 -4
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_engine.py +48 -0
- tariffkit-0.4.0/tests/test_ha_backfill.py +890 -0
- tariffkit-0.4.0/tests/test_ha_bank.py +411 -0
- tariffkit-0.4.0/tests/test_ha_energy.py +965 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_integrations.py +25 -2
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_interop.py +39 -14
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_ledger.py +63 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_mqtt_publisher.py +54 -5
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_schedules.py +155 -16
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_trueup.py +142 -1
- tariffkit-0.2.3/CHANGELOG.md +0 -247
- tariffkit-0.2.3/tests/conftest.py +0 -26
- {tariffkit-0.2.3 → tariffkit-0.4.0}/.gitignore +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/LICENSE +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/account/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/account/errors.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/account/rates.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/account/repository.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/cli.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/cca/mce/2023-01-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/cca/mce/2026-04-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/export/pge/acc_plus/2023-04-15.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/export/pge/nbt00.json.gz +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/export/pge/nbt23.json.gz +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/export/pge/nbt24.json.gz +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/export/pge/nbt25.json.gz +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/export/pge/nbt26.json.gz +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/holidays.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/manifest.json +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/nsc/pge.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/eelec/2025-01-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/eelec/2025-03-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/eelec/2025-09-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/eelec/2026-01-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/eelec/2026-03-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/ev2a/2025-01-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/ev2a/2025-03-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/ev2a/2025-09-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/ev2a/2026-01-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tariff/pge/ev2a/2026-03-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tax/ca_energy_resources/2025-01-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/tax/ca_energy_resources/2026-01-01.toml +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/data/versioned.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/errors.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/export/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/export/nbt.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/interop/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/interop/emhass.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/interop/slots.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/mqtt/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/providers/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/providers/pge/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/providers/pge/statements/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/providers/pge/statements/errors.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/providers/pge/statements/model.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/providers/pge/statements/ocr.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/py.typed +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/secrets.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/sources/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/sources/greenbutton.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/sources/homeassistant.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/sources/influx.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/sources/pge.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/tariff/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/timeutil.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/web/__init__.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/src/tariffkit/web/app.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/fixtures/nbt_golden_rows.jsonl +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/fixtures/statements/synthetic_cca_ratechange.txt +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_ev2a.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_export_golden.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_mce.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_pge_reconcile.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_pge_statements.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_private_config.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_sources_greenbutton.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_sources_ha.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_sources_influx.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_sources_pge.py +0 -0
- {tariffkit-0.2.3 → tariffkit-0.4.0}/tests/test_versioned.py +0 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. This project follows
|
|
4
|
+
[Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.4.0] - 2026-08-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `tariffkit.billing.run_lifetime` folds a run of bills from end to end,
|
|
12
|
+
applying each annual settlement in the order it falls and returning a
|
|
13
|
+
`LifetimeLedger` that reports what every cycle opened with. `run_ledger`
|
|
14
|
+
carries a bank between cycles but knows nothing about the year closing on it,
|
|
15
|
+
and `run_true_ups` computes each event independently of the others -- so
|
|
16
|
+
composing them naively lets a second settlement be derived from a ledger that
|
|
17
|
+
never saw the first one's clawback. This sequencing had grown inside the Home
|
|
18
|
+
Assistant integration, where it was neither tested against tariff text nor
|
|
19
|
+
visible to anyone reading the library.
|
|
20
|
+
|
|
21
|
+
- `Bill.import_charges` (energy charges plus the statutory taxes beside them),
|
|
22
|
+
`Bill.marginal_buckets` (a span's time-of-use split, from two cycle-to-date
|
|
23
|
+
bills), `CreditBalances.held_by` (which buckets each settling party holds),
|
|
24
|
+
`AccountProfile.pto_date` (the earliest Permission To Operate any epoch
|
|
25
|
+
records), and `TrueUp.settles`. Each replaces a copy that had accumulated in
|
|
26
|
+
the Home Assistant integration; `held_by`'s copy had come to disagree with
|
|
27
|
+
`tariffkit.billing.trueup` about who owns the generation bucket.
|
|
28
|
+
|
|
29
|
+
- `check_coverage` accepts `netted=True` for readings that come from a meter's
|
|
30
|
+
own import and export registers, and `through=<moment>` for a period still
|
|
31
|
+
running. Callers who knew these facts were filtering the function's messages
|
|
32
|
+
by their text, which stops filtering the moment a new warning is added -- as
|
|
33
|
+
one was.
|
|
34
|
+
|
|
35
|
+
`through` also closes a hole that predates the flag. A missing hour at the
|
|
36
|
+
*end* of a series is not a gap between readings -- a gap needs a reading on
|
|
37
|
+
each side, and the whole point of a series that has stopped is that there is
|
|
38
|
+
nothing on the far side -- so only a clock can tell an hour that arrived
|
|
39
|
+
empty from one that has not arrived. Without one, a meter that stopped
|
|
40
|
+
reporting went on producing a smaller figure that still called itself
|
|
41
|
+
complete, indefinitely. `check_coverage` now measures the shortfall against
|
|
42
|
+
elapsed time and names a series that has stopped, and the Home Assistant
|
|
43
|
+
entities pass their clock so the running totals get both.
|
|
44
|
+
|
|
45
|
+
- The Home Assistant integration can optionally track what the meter actually
|
|
46
|
+
moved. Name the cumulative grid-import and grid-export kWh entities under
|
|
47
|
+
**Configure → Metered energy** — deliberately not part of initial setup,
|
|
48
|
+
since pricing needs no meter and the counters are usually integrated after
|
|
49
|
+
the tariff — and it adds running Energy Cost, Export Credit, and Amount Due
|
|
50
|
+
entities for today and for the billing cycle to date, alongside Grid Import
|
|
51
|
+
and Grid Export totals.
|
|
52
|
+
|
|
53
|
+
Amount Due is what a statement would charge, not what a bill sums to. Under
|
|
54
|
+
Net Billing a cycle earning more credit than it owes does not produce a
|
|
55
|
+
refund: the excess banks, and a credit may only offset charges the tariff
|
|
56
|
+
lets it reach, so Non-Bypassable Charges stay due however large the bank. The
|
|
57
|
+
figure comes from `tariffkit.billing.apply_credits`; `credit_applied` and
|
|
58
|
+
`bank_change` attributes say where the difference went. Reading `Bill.total`
|
|
59
|
+
instead went negative on a heavily exporting cycle, which no statement does.
|
|
60
|
+
|
|
61
|
+
Where the bank cannot be trusted -- not folded yet, unreadable, or folded
|
|
62
|
+
across a gap -- the figures are stated before any bank offsets them, the
|
|
63
|
+
reason appears in `warnings`, and `quality.complete` is false. Silently using
|
|
64
|
+
a doubtful balance halved a cycle's charge while reporting itself complete.
|
|
65
|
+
|
|
66
|
+
The counters do not have to reset daily: each hour's energy comes from the
|
|
67
|
+
recorder's own long-term statistics, which already absorb counter restarts
|
|
68
|
+
and reload gaps, with the hour in progress read live off entity state.
|
|
69
|
+
Readings are priced by `tariffkit.billing.BillEngine`, the same code that
|
|
70
|
+
reconciles a printed statement, so the running figures carry time-of-use
|
|
71
|
+
bucketing, the Energy Commission Tax, the baseline credit, the whole day's
|
|
72
|
+
Base Services Charge, and the rule that exports before Permission To Operate
|
|
73
|
+
earn nothing.
|
|
74
|
+
|
|
75
|
+
Today's figures are the cycle's movement across today rather than a one-day
|
|
76
|
+
bill, because parts of a bill are cumulative over a cycle rather than
|
|
77
|
+
additive over its days -- the baseline allowance is granted per cycle and
|
|
78
|
+
consumed in day order, so pricing a day alone grants it one day's allowance
|
|
79
|
+
however much the cycle had banked. Gaps, overlaps and reconstructed intervals
|
|
80
|
+
in the metered series are reported in `warnings` and clear
|
|
81
|
+
`quality.complete`, so a recorder outage understates the figure loudly rather
|
|
82
|
+
than quietly.
|
|
83
|
+
|
|
84
|
+
A **Billing cycle start day** setting names the day of the month the meter is
|
|
85
|
+
read, but it is only a fallback: where the profile carries imported
|
|
86
|
+
statements the cycle boundary comes from the statements themselves, which is
|
|
87
|
+
the only way to match a real bill -- PG&E reads on business days, so
|
|
88
|
+
consecutive cycles open on the 29th, the 30th, the 1st and the 3rd. A
|
|
89
|
+
`cycle_boundary` attribute reports which was used.
|
|
90
|
+
|
|
91
|
+
Naming no entities creates none of these, leaving every existing entity
|
|
92
|
+
byte-identical. One exception is worth knowing: an account profile imported
|
|
93
|
+
from the CLI carries its own `meter_sources.ha` mapping, and that mapping is
|
|
94
|
+
honoured, so such an entry gains the entities without anyone opening the
|
|
95
|
+
form.
|
|
96
|
+
|
|
97
|
+
- A `tariffkit.backfill_usage` action prices metered history into long-term
|
|
98
|
+
statistics, so cost and credit for days before the meters were configured
|
|
99
|
+
appear in Home Assistant rather than only through the CLI. It writes external
|
|
100
|
+
statistics under a `tariffkit:` namespace -- the shape `opower` uses for
|
|
101
|
+
utility history -- which leaves the running entities' own series and the
|
|
102
|
+
recorder's compilation of them untouched. One row per finished day, each being
|
|
103
|
+
its cycle's movement across that day, so the days sum to what their cycle
|
|
104
|
+
actually charged. The run is folded through every annual settlement it
|
|
105
|
+
crosses, not merely carried forward, because a true-up claws back credit
|
|
106
|
+
already paid out as Net Surplus Compensation -- so a cycle after an
|
|
107
|
+
anniversary opens with less bank than a straight fold would give it, which is
|
|
108
|
+
what the live entities have always done.
|
|
109
|
+
It defaults to the billing cycle containing the PTO date -- where bills begin
|
|
110
|
+
meaning anything, since Net Billing compensation runs from Permission To
|
|
111
|
+
Operate -- and reports a per-cycle bill alongside the daily rows, which is
|
|
112
|
+
what an export credit ledger folds.
|
|
113
|
+
Rerunning replaces the window rather than appending to it, which is how a
|
|
114
|
+
corrected account history is picked up; a rerun over a later window continues
|
|
115
|
+
the running total it finds rather than restarting it. Only days the recorder
|
|
116
|
+
actually holds readings for are priced, and coverage is judged per meter, so
|
|
117
|
+
one direction cannot vouch for the other.
|
|
118
|
+
|
|
119
|
+
- **Export credit bank** entities carry the Net Billing credit balance
|
|
120
|
+
between cycles, which no entity computing forward from the day meters were
|
|
121
|
+
configured can know. It folds every closed cycle since the one containing the
|
|
122
|
+
PTO date -- so it opens at zero by construction, with no balance anyone has to
|
|
123
|
+
supply -- applying each cycle's credits against its charges through
|
|
124
|
+
`tariffkit.billing.run_ledger`, and continuing from a true-up's own closing
|
|
125
|
+
balance where the run crosses one. It refuses rather than reports where the
|
|
126
|
+
run has a gap: the library's ledger deliberately does not check for one, and a
|
|
127
|
+
bank folded across a missing cycle reports a balance that never existed. The
|
|
128
|
+
balance is recomputed when a cycle closes rather than accumulated, so
|
|
129
|
+
correcting account history fixes it instead of leaving a stored figure quietly
|
|
130
|
+
wrong. There are two entities because a Community Choice Aggregator account
|
|
131
|
+
has two banks -- the utility's delivery and bonus credits against the CCA's
|
|
132
|
+
export credit -- settling on unrelated calendars, which a single total would
|
|
133
|
+
merge into a figure no statement prints. Every annual settlement in a run is
|
|
134
|
+
applied in order rather than only the most recent, since each is computed
|
|
135
|
+
independently and a later one cannot see an earlier one's clawback. A
|
|
136
|
+
settlement that reverses and pays nothing -- the utility's, on a CCA account
|
|
137
|
+
-- is recorded without consuming cycles, so it cannot shorten the other
|
|
138
|
+
supplier's cash-out year and leave unreversed credit in the bank.
|
|
139
|
+
|
|
140
|
+
### Changed
|
|
141
|
+
- **Only affects development checkouts.** The metered-energy entities below are
|
|
142
|
+
new in this release, so no published version ever carried their earlier
|
|
143
|
+
names; this note is for anyone who ran them from `main` before the rename.
|
|
144
|
+
See [Upgrading](docs/home-assistant.md#upgrading). The
|
|
145
|
+
Net Cost entities are renamed **Amount Due** (`net_cost_today` /
|
|
146
|
+
`net_cost_cycle` become `amount_due_today` / `amount_due_cycle`), and the
|
|
147
|
+
backfill's `tariffkit:<profile>_net_cost` statistic becomes
|
|
148
|
+
`tariffkit:<profile>_amount_due`.
|
|
149
|
+
|
|
150
|
+
The rename is the point, not a side effect. The figure changed meaning and
|
|
151
|
+
sign -- it was charges less every credit earned, which goes negative; it is
|
|
152
|
+
now what a statement charges, which does not. Home Assistant's statistics
|
|
153
|
+
compiler accumulates a `total` sensor as `sum += new - old` whenever
|
|
154
|
+
`last_reset` is unchanged, and the cycle entity's `last_reset` is the cycle
|
|
155
|
+
start, so keeping the old id would have added the whole banked balance to the
|
|
156
|
+
lifetime sum in a single compile and never washed it out. A new unique id
|
|
157
|
+
abandons the old series intact rather than corrupting it.
|
|
158
|
+
|
|
159
|
+
Update any dashboard card, template, or automation that names the old
|
|
160
|
+
entities, and re-run the backfill: history published by the older code was
|
|
161
|
+
priced as `Bill.total` and without the annual settlements. The old
|
|
162
|
+
`net_cost_*` entities are removed from the registry on reload; their recorded
|
|
163
|
+
statistics remain and can be deleted under **Developer tools -> Statistics**.
|
|
164
|
+
|
|
165
|
+
### Fixed
|
|
166
|
+
- `run_true_ups` no longer emits a Community Choice Aggregator cash-out for a
|
|
167
|
+
bundled account. There is no aggregator to settle with, and on such an
|
|
168
|
+
account PG&E supplies generation, so the cash-out and the Relevant Period
|
|
169
|
+
each clawed back the same generation credit for the same exported energy.
|
|
170
|
+
|
|
171
|
+
- `apply_credits` no longer reports a negative `cash_due`. `non_offsettable`
|
|
172
|
+
can go negative on its own -- `baseline_credit` is a negative import
|
|
173
|
+
component listed there -- and at a high export-to-import ratio it outweighs
|
|
174
|
+
the charges beside it. Credit that cannot be spent stays in the bank instead.
|
|
175
|
+
|
|
176
|
+
- Statement evidence is identified by what a statement says rather than by the
|
|
177
|
+
bytes it arrived in, so re-importing evidence a profile already holds is a
|
|
178
|
+
no-op. The utility regenerates a bill PDF on every request -- the same
|
|
179
|
+
statement downloaded twice is byte-different and hashes differently -- and
|
|
180
|
+
`AccountObservation.identity()` preferred that digest, so nothing a profile
|
|
181
|
+
held ever matched and every `account sync --apply` appended its whole window
|
|
182
|
+
again. Profiles written before this collapse their repeats the next time they
|
|
183
|
+
are loaded, so no migration is needed. `source_digest` remains as provenance,
|
|
184
|
+
and a profile still refuses to hold two observations that name the same source
|
|
185
|
+
document while disagreeing about what it says -- keyed on the top-level digest
|
|
186
|
+
falling back to the agreements' own, so an observation carrying only the
|
|
187
|
+
latter is still guarded. The extraction mode is excluded from identity
|
|
188
|
+
alongside the digest: the parser falls back to OCR for older statements, and
|
|
189
|
+
how a document was read is not part of what it says.
|
|
190
|
+
- `tariffkit account sync` now signs in before asking the portal for the
|
|
191
|
+
statement list. A resumed session arrives with a live session cookie and no
|
|
192
|
+
CSRF token, because the token is one-shot and deliberately not cached, so the
|
|
193
|
+
first authenticated call failed -- either as a bare "the session token is
|
|
194
|
+
stale" or, when the portal answered with an empty list instead of an error, as
|
|
195
|
+
a silent "received 0 statement update(s)" against an account with 25
|
|
196
|
+
statements. `apex`'s own recovery could not rescue it: it falls back to a
|
|
197
|
+
forced re-login, which fails while already signed in because the login page
|
|
198
|
+
redirects to the community and the token it carries belongs to the wrong
|
|
199
|
+
Lightning app. `audit doctor` was unaffected because it calls `login()` first,
|
|
200
|
+
which is what made the two disagree.
|
|
201
|
+
|
|
202
|
+
## [0.3.0] - 2026-08-22
|
|
203
|
+
|
|
204
|
+
### Added
|
|
205
|
+
- PG&E's complete active single-family residential lineup is now covered:
|
|
206
|
+
generated E-1 and E-TOU-D snapshots join E-ELEC, E-TOU-C, and EV2-A.
|
|
207
|
+
E-1 preserves tiered baseline billing while exposing the over-baseline
|
|
208
|
+
marginal price, and E-TOU-D observes its weekday-only 5–8 p.m. peak and
|
|
209
|
+
tariff holiday calendar.
|
|
210
|
+
- Generated, effective-dated D-CARE, D-MEDICAL, Rule 19 Medical Baseline, and
|
|
211
|
+
E-RSMART data now drive residential program adjustments. SmartRate accepts
|
|
212
|
+
explicit announced event dates and marks prices beyond the authoritative
|
|
213
|
+
event horizon incomplete rather than guessing future events.
|
|
214
|
+
- Prices now decompose into a fixed set of chartable component groups —
|
|
215
|
+
generation, distribution, transmission, surcharges, credits, and a catch-all
|
|
216
|
+
other on the import side; generation, delivery, credits, and other on the
|
|
217
|
+
export side. The groups sum back to the price they came from and do not vary
|
|
218
|
+
with the tariff, supplier, or discount, so a chart built against them
|
|
219
|
+
survives an account change. `ImportPrice.grouped()`, `ExportPrice.grouped()`,
|
|
220
|
+
a `groups` key in every `to_dict` payload, and `tariffkit.components` expose
|
|
221
|
+
them to library and REST callers.
|
|
222
|
+
- Home Assistant gains a sensor per component group in each direction, and the
|
|
223
|
+
forecast's `rates` attribute carries the same roll-up per hour, so both the
|
|
224
|
+
recorded past and the next 48 hours can be drawn as stacked charts -- one
|
|
225
|
+
card each, because stacking recorded and forecast points together would
|
|
226
|
+
double-count the current hour. The MQTT publisher publishes the same series
|
|
227
|
+
with matching discovery payloads, each band carrying its price's quality
|
|
228
|
+
flags.
|
|
229
|
+
- Home Assistant now exposes the AB 205 Base Services Charge as **Daily Fixed
|
|
230
|
+
Charge** in `USD/day`. The unit keeps it out of the Energy dashboard's price
|
|
231
|
+
pickers and out of any `USD/kWh` stack, which is why it can be published at
|
|
232
|
+
all: it is a fixed daily amount, not a marginal price.
|
|
233
|
+
|
|
234
|
+
### Changed
|
|
235
|
+
- Home Assistant now offers every active PG&E residential schedule and
|
|
236
|
+
Medical Baseline configuration while preserving existing profile and entity
|
|
237
|
+
identities.
|
|
238
|
+
|
|
239
|
+
### Fixed
|
|
240
|
+
- Predbat attributes now use its `from` / `to` / `rate` contract for values
|
|
241
|
+
already expressed in cents. Predbat no longer interprets TariffKit's cents as
|
|
242
|
+
currency units and multiplies them by 100, while Pacific-midnight anchoring
|
|
243
|
+
and complete 46-, 48-, and 50-slot tariff days remain unchanged.
|
|
244
|
+
|
|
245
|
+
## [0.2.3] - 2026-08-17
|
|
246
|
+
|
|
247
|
+
### Changed
|
|
248
|
+
- Pacific Gas and Electric now has the unambiguous machine identifier
|
|
249
|
+
`pacific_gas_and_electric`, while Home Assistant and MQTT present `PG&E` or
|
|
250
|
+
the full company name. The separate `pge` identifier correctly means Portland
|
|
251
|
+
General Electric and is recognized but explicitly unsupported for pricing,
|
|
252
|
+
preventing it from ever selecting California tariff data.
|
|
253
|
+
- Home Assistant now labels the export-minus-import calculation explicitly,
|
|
254
|
+
translates time-of-use states for display, and presents the forecast horizon
|
|
255
|
+
as **Rates Available Through**. Forecast metadata and a new **Rate Data
|
|
256
|
+
Status** entity are grouped under diagnostics, where PTO date, export lock
|
|
257
|
+
end, NBT vintage, tariff provenance, source, and quality flags explain the
|
|
258
|
+
active rates without crowding the primary price controls.
|
|
259
|
+
|
|
260
|
+
### Fixed
|
|
261
|
+
- The PyPI project page now loads the TariffKit banner from an absolute URL
|
|
262
|
+
instead of an unresolved repository-relative path.
|
|
263
|
+
|
|
264
|
+
## [0.2.2] - 2026-08-16
|
|
265
|
+
|
|
266
|
+
### Added
|
|
267
|
+
- Project documentation now uses a TariffKit banner built from the integration's
|
|
268
|
+
existing icon and a provider-neutral electricity rate curve.
|
|
269
|
+
- Public contribution and support guidance now provides privacy-safe issue
|
|
270
|
+
forms, private security routing, review ownership, a pull request checklist,
|
|
271
|
+
community conduct expectations, and exact development checks. The guidance
|
|
272
|
+
also makes the generated-data and repository-only audit boundaries explicit
|
|
273
|
+
so public collaboration does not expose utility-account material or alter
|
|
274
|
+
distribution guarantees.
|
|
275
|
+
- Pull requests now run GitHub's dependency review action with read-only
|
|
276
|
+
permissions so vulnerable or disallowed dependency changes fail before merge.
|
|
277
|
+
|
|
278
|
+
### Changed
|
|
279
|
+
- **Python 3.14.2 and Home Assistant 2026.3.0 are now the supported floors.**
|
|
280
|
+
The lockfile no longer carries the obsolete Home Assistant 2026.2 fallback,
|
|
281
|
+
and CI audits the complete locked dependency graph with a pinned `pip-audit`
|
|
282
|
+
release while retaining raw reports as failure artifacts. The Linux secrets
|
|
283
|
+
extra and Home Assistant tests both inherit the same exact cryptography pin;
|
|
284
|
+
an expiring policy requires each audit to report exactly its three known,
|
|
285
|
+
unreachable advisories and rejects any additional finding.
|
|
286
|
+
- The README now explains that default HACS approval may take months and gives
|
|
287
|
+
complete custom-repository installation steps for use during the review.
|
|
288
|
+
- Workflow actions are pinned to reviewed immutable commits, checkout credentials
|
|
289
|
+
are not persisted, superseded runs are bounded by concurrency controls, and
|
|
290
|
+
repository write access is isolated to the jobs that publish results.
|
|
291
|
+
- MQTT now rejects credentials over plaintext unless the operator explicitly
|
|
292
|
+
allows insecure authentication for an isolated trusted network. Passwords
|
|
293
|
+
also require a username, while anonymous plaintext publishing remains valid.
|
|
294
|
+
|
|
295
|
+
### Fixed
|
|
296
|
+
- Rate-sheet regeneration now scans trailing table cells with a linear parser,
|
|
297
|
+
avoiding pathological regular-expression backtracking on malformed publisher
|
|
298
|
+
text while preserving accepted dollar, decimal, negative, and change-marker
|
|
299
|
+
forms.
|
|
300
|
+
|
|
301
|
+
## [0.2.1] - 2026-08-16
|
|
302
|
+
|
|
303
|
+
### Added
|
|
304
|
+
- **HACS releases now include a deterministic `tariffkit.zip` integration
|
|
305
|
+
artifact.** HACS installs only the tracked component files, rooted directly
|
|
306
|
+
in the integration directory, while the release pipeline validates the ZIP
|
|
307
|
+
against its source, checksums it with the Python distributions, and attaches
|
|
308
|
+
all artifacts before immutable publication. Dedicated HACS and hassfest
|
|
309
|
+
checks also gate integration changes and prepare TariffKit for default-store
|
|
310
|
+
submission.
|
|
311
|
+
|
|
312
|
+
### Fixed
|
|
313
|
+
- Home Assistant action descriptions now follow hassfest's current service
|
|
314
|
+
schema: icons live in `icons.json`, target metadata is explicit, and the
|
|
315
|
+
config-entry-only YAML schema is declared.
|
|
316
|
+
|
|
317
|
+
## [0.2.0] - 2026-08-16
|
|
318
|
+
|
|
319
|
+
### Added
|
|
320
|
+
- **Named, effective-dated account profiles** track tariff, supplier, baseline,
|
|
321
|
+
export, and credential-set changes over a service agreement's lifetime.
|
|
322
|
+
Pricing and billing resolve the settings in force at each timestamp, including
|
|
323
|
+
cycles that cross an account transition. Profile writes are atomic,
|
|
324
|
+
revision-checked, and locked against concurrent updates.
|
|
325
|
+
- **PG&E statement import and portal synchronization** can populate an account
|
|
326
|
+
profile from printed facts. Proposed changes are reported as additions,
|
|
327
|
+
confirmations, conflicts, or missing required values; a statement with a
|
|
328
|
+
conflict cannot be partially applied. Statement support is available through
|
|
329
|
+
the `statements` extra, while the account-specific audit harness remains
|
|
330
|
+
repository-only.
|
|
331
|
+
- **Profile-scoped meter sources** store provider-neutral grid-import and
|
|
332
|
+
grid-export mappings for Home Assistant and InfluxDB 3. The bill command can
|
|
333
|
+
query either source directly, with explicit command options taking precedence
|
|
334
|
+
over profile, environment, and global defaults.
|
|
335
|
+
- **Credential sets backed by the operating-system keyring** hold PG&E, Home
|
|
336
|
+
Assistant, InfluxDB, and MQTT secrets outside configuration files and command
|
|
337
|
+
arguments. Environment injection remains available for containers.
|
|
338
|
+
- **A complete billing layer** now covers interval netting, coverage warnings,
|
|
339
|
+
baseline credits, fixed charges, taxes, export-credit buckets, annual true-up,
|
|
340
|
+
and CCA cash-out. Credits retain their printed statement scope instead of
|
|
341
|
+
being treated as one fungible balance.
|
|
342
|
+
- **Home Assistant and InfluxDB 3 interval sources** complement the Green Button
|
|
343
|
+
reader. Home Assistant reads long-term statistics and prefers five-minute
|
|
344
|
+
data; InfluxDB derives exact totals from cumulative-counter endpoints and
|
|
345
|
+
spreads advances across the time in which they accrued.
|
|
346
|
+
- **E-TOU-C and EV2-A retail schedules**, complete PCIA and franchise-fee
|
|
347
|
+
vintage tables, E-TOU-C baseline allowances, California's electrical-energy
|
|
348
|
+
surcharge, Net Surplus Compensation data, and effective-dated PG&E and MCE
|
|
349
|
+
rate snapshots extend pricing and billing beyond the original E-ELEC
|
|
350
|
+
schedule.
|
|
351
|
+
- **Repository-only rate-data generators** rebuild every vendored dataset from
|
|
352
|
+
its published source and read the rendered result back through runtime code
|
|
353
|
+
before writing it. A weekly workflow checks export matrices, retail tariffs,
|
|
354
|
+
ACC Plus, CCA cards, Net Surplus Compensation, holidays, and the state
|
|
355
|
+
surcharge for upstream changes.
|
|
356
|
+
- **Home Assistant account-history flows** support staged initial setup and
|
|
357
|
+
profile inspection, transition editing, statement import, and sanitized
|
|
358
|
+
profile export. Stable profile-based config-entry identity survives tariff and
|
|
359
|
+
supplier changes.
|
|
360
|
+
- **Home Assistant response actions** provide current or forecast rates in
|
|
361
|
+
native and EMHASS shapes for caller-selected windows. Requests reject
|
|
362
|
+
ambiguous DST-fold timestamps, misaligned windows, and horizons beyond seven
|
|
363
|
+
days rather than guessing.
|
|
364
|
+
- **Home Assistant diagnostics and integration tests** cover config and options
|
|
365
|
+
flows, migration, entities, actions, Energy dashboard compatibility,
|
|
366
|
+
effective-dated provenance, DST handling, and opt-in Predbat output.
|
|
367
|
+
Diagnostics deliberately omit account history, observations, credentials,
|
|
368
|
+
and meter mappings.
|
|
369
|
+
- **Pure interoperability adapters** generate EMHASS, Predbat, and generic slot
|
|
370
|
+
payloads from a `PriceCurve`, shared by Home Assistant and MQTT without either
|
|
371
|
+
integration becoming a dependency.
|
|
372
|
+
- **Request-scoped REST configuration and account selection** allow callers to
|
|
373
|
+
price one request against validated settings or a named profile. Unknown
|
|
374
|
+
configuration keys and credential fields are rejected.
|
|
375
|
+
- **A bind-mounted Home Assistant development stack** runs the custom component
|
|
376
|
+
and local package source in a real container without publishing a wheel.
|
|
377
|
+
- **A build-once release process and maintainer runbook** synchronize Python,
|
|
378
|
+
lockfile, Home Assistant, changelog, and documentation versions. One validated
|
|
379
|
+
wheel/sdist pair moves through optional TestPyPI staging, protected PyPI
|
|
380
|
+
approval, PEP 740 attestations, and an immutable GitHub release.
|
|
381
|
+
|
|
382
|
+
### Changed
|
|
383
|
+
- **The project is now TariffKit.** The distribution, import package, CLI,
|
|
384
|
+
configuration directory, environment prefix, MQTT namespace, repository
|
|
385
|
+
links, and Home Assistant domain use the utility-neutral `tariffkit` identity.
|
|
386
|
+
The initial data-provider scope remains PG&E and California.
|
|
387
|
+
- **Packaging remains one public distribution in one repository.** Optional
|
|
388
|
+
features use extras and lazy imports, leaving the default runtime
|
|
389
|
+
dependency-free. Rate-data tooling and the account-specific audit harness are
|
|
390
|
+
excluded from wheel and sdist but retain strict lint, typing, and test
|
|
391
|
+
coverage.
|
|
392
|
+
- **Python 3.14 is the minimum**, and Home Assistant 2026.3.0 is therefore the
|
|
393
|
+
integration floor. Development dependencies use PEP 735 groups, package
|
|
394
|
+
licensing uses an SPDX expression, and installed metadata is the runtime
|
|
395
|
+
version source.
|
|
396
|
+
- **Home Assistant installs the exact matching TariffKit release** instead of
|
|
397
|
+
vendoring package source. Its config flow asks only questions made relevant
|
|
398
|
+
by earlier answers and separates account history from forecast and Predbat
|
|
399
|
+
options.
|
|
400
|
+
- **Home Assistant entities are lean and recorder-safe.** Import and export
|
|
401
|
+
price sensors remain compatible with the Energy dashboard, a timestamp entity
|
|
402
|
+
reports forecast coverage, and large forecast or optimizer payloads are not
|
|
403
|
+
recorded every minute. The fixed daily charge is no longer represented as a
|
|
404
|
+
marginal price sensor.
|
|
405
|
+
- **EMHASS forecasts are requested through an action and Predbat output is
|
|
406
|
+
opt-in.** Neither large payload is computed and attached to every entity when
|
|
407
|
+
unused.
|
|
408
|
+
- **Pricing provenance and quality are preserved end to end.** Core, CLI, REST,
|
|
409
|
+
MQTT, and Home Assistant outputs retain `locked`, `exact`, and `complete`
|
|
410
|
+
states and describe contiguous provenance segments across effective-date
|
|
411
|
+
boundaries.
|
|
412
|
+
- **Green Button names now describe the format rather than its container.**
|
|
413
|
+
`read_green_button`, `GreenButtonLayout`, and `--source green-button` replace
|
|
414
|
+
generic CSV terminology; the legacy source spelling remains accepted.
|
|
415
|
+
- **Strict typing now covers the Home Assistant custom component and all
|
|
416
|
+
repository tools**, while Ruff enforces function annotations throughout the
|
|
417
|
+
repository.
|
|
418
|
+
|
|
419
|
+
### Fixed
|
|
420
|
+
- Production publishing no longer inherits a false implicit failure from the
|
|
421
|
+
intentionally skipped optional TestPyPI job. The explicit release dependency
|
|
422
|
+
checks now permit protected PyPI and GitHub publication only after the build
|
|
423
|
+
and draft jobs themselves succeed.
|
|
424
|
+
- Account-profile updates no longer race between revision checks and filesystem
|
|
425
|
+
mutation, statement-derived histories reject gaps and overlaps, imported Home
|
|
426
|
+
Assistant profiles cannot replace an entry's stable identity, and MQTT
|
|
427
|
+
profile selection respects environment precedence.
|
|
428
|
+
- Baseline credits use each day and vintage's own rate, pre-PTO exports earn no
|
|
429
|
+
compensation, string supplier values are normalized before branching, and
|
|
430
|
+
historical pricing never borrows data from a future vintage.
|
|
431
|
+
- CCA generation rates are selected by PG&E schedule instead of silently using
|
|
432
|
+
E-ELEC values for every plan. PCIA, franchise-fee, MCE generation, cost-relief,
|
|
433
|
+
and premium data are resolved from their actual effective vintages.
|
|
434
|
+
- Billing coverage and interval stepping use absolute time across both DST
|
|
435
|
+
transitions. Repeated autumn timestamps are disambiguated during ingest, rate
|
|
436
|
+
points remain contiguous, and MQTT no longer sleeps through the second 01:00
|
|
437
|
+
hour.
|
|
438
|
+
- Green Button ingestion handles account preambles, split date/time columns,
|
|
439
|
+
unit-suffixed headers, explicit offsets, and repeated autumn wall times.
|
|
440
|
+
Home Assistant and InfluxDB sources report implausible resets and missing
|
|
441
|
+
coverage instead of inventing plausible energy.
|
|
442
|
+
- Export-rate lookups use each vintage's own holiday calendar and surface
|
|
443
|
+
publisher drift beyond the verified exact range rather than silently claiming
|
|
444
|
+
exact future values.
|
|
445
|
+
- Generator parsing no longer drops a PCIA row joined to the next page header,
|
|
446
|
+
and all generated files are validated through independent runtime readers
|
|
447
|
+
before replacing vendored data.
|
|
448
|
+
|
|
449
|
+
## [0.1.0] - 2026-07-28
|
|
450
|
+
|
|
451
|
+
Initial release.
|
|
452
|
+
|
|
453
|
+
### Added
|
|
454
|
+
- `RateEngine` with `price_now()`, `price_at()`, and `forecast()` for PG&E
|
|
455
|
+
E-ELEC import prices and NEM 3.0 / Net Billing Tariff export credits.
|
|
456
|
+
- Vendored rate data for all five NBT vintages (NBT23/24/25/26/00), collapsed
|
|
457
|
+
from PG&E's ~40 MB-per-vintage hourly files to 268 KiB total with verified
|
|
458
|
+
lossless round-tripping.
|
|
459
|
+
- ACC Plus adder as a first-class, separately reported component.
|
|
460
|
+
- CCA / Direct Access support: bundled generation and PCIA are dropped, and
|
|
461
|
+
delivery-only prices are flagged `complete=False` rather than understated.
|
|
462
|
+
- CLI: `now`, `forecast`, `info`, `mqtt`, `serve`.
|
|
463
|
+
- MQTT publisher with Home Assistant MQTT Discovery and a last-will
|
|
464
|
+
availability topic.
|
|
465
|
+
- FastAPI service under the `web` extra.
|
|
466
|
+
- HACS-installable Home Assistant custom component.
|
|
467
|
+
- `tools/regen_data.py` for refreshing vendored data, plus a weekly CI job that
|
|
468
|
+
fails when upstream rates change.
|
|
469
|
+
|
|
470
|
+
### Notes on upstream data
|
|
471
|
+
- PG&E's export files label the repeated 01:00 on the autumn DST transition as
|
|
472
|
+
`HS2`, so that hour is priced as 2am. Handled in `timeutil.export_hour`.
|
|
473
|
+
- From 2036 onward PG&E's own hour labels stop tracking Pacific daylight time
|
|
474
|
+
and NBT25/26/00 duplicate some holidays onto the following day. The verified
|
|
475
|
+
boundary is recorded per vintage as `exact_through` and surfaced as
|
|
476
|
+
`ExportPrice.exact`. Every year within a nine-year rate lock is exact.
|
|
477
|
+
- Holiday calendars are extracted per vintage from the source data rather than
|
|
478
|
+
recomputed, because the vintage files disagree in far-future years.
|
|
479
|
+
|
|
480
|
+
[Unreleased]: https://github.com/eman/tariffkit/compare/v0.4.0...HEAD
|
|
481
|
+
[0.4.0]: https://github.com/eman/tariffkit/releases/tag/v0.4.0
|
|
482
|
+
[0.3.0]: https://github.com/eman/tariffkit/releases/tag/v0.3.0
|
|
483
|
+
[0.2.3]: https://github.com/eman/tariffkit/releases/tag/v0.2.3
|
|
484
|
+
[0.2.2]: https://github.com/eman/tariffkit/releases/tag/v0.2.2
|
|
485
|
+
[0.2.1]: https://github.com/eman/tariffkit/releases/tag/v0.2.1
|
|
486
|
+
[0.2.0]: https://github.com/eman/tariffkit/releases/tag/v0.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tariffkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Offline electricity tariff pricing, billing, and integration tools
|
|
5
5
|
Project-URL: Homepage, https://github.com/eman/tariffkit
|
|
6
6
|
Project-URL: Documentation, https://github.com/eman/tariffkit#documentation
|
|
@@ -52,15 +52,17 @@ Description-Content-Type: text/markdown
|
|
|
52
52
|
[](https://github.com/eman/tariffkit/actions/workflows/ci.yml)
|
|
53
53
|
[](https://pypi.org/project/tariffkit/)
|
|
54
54
|
[](https://www.python.org/downloads/)
|
|
55
|
-
[](LICENSE)
|
|
55
|
+
[](https://github.com/eman/tariffkit/blob/main/LICENSE)
|
|
56
56
|
|
|
57
57
|
An offline electricity tariff engine for pricing, billing, and energy-system
|
|
58
58
|
integrations. The first data provider supports PG&E residential rate plans under
|
|
59
59
|
**NEM 3.0 / the Net Billing Tariff (NBT)**; the package identity is deliberately
|
|
60
60
|
not tied to one utility or tariff program.
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
The complete active single-family residential portfolio is vendored:
|
|
63
|
+
**E-1**, **E-ELEC**, **E-TOU-C**, **E-TOU-D**, and **EV2-A**. CARE, FERA,
|
|
64
|
+
Medical Baseline/D-MEDICAL, and event-injected SmartRate adjustments are
|
|
65
|
+
modeled separately from their underlying plans.
|
|
64
66
|
|
|
65
67
|
Under NBT your export credit is not a time-of-use schedule: it is an hourly
|
|
66
68
|
Avoided Cost Calculator value that swings from about $0.06/kWh at midday to
|
|
@@ -92,9 +94,9 @@ component. `tariffkit` collapses it at build time, verifying losslessness cell b
|
|
|
92
94
|
cell, so the entire five-vintage dataset ships inside the wheel at **268 KiB**
|
|
93
95
|
and every lookup is a few list indexes.
|
|
94
96
|
|
|
95
|
-
The retail side is similar:
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
The retail side is similar: generated effective-dated tables hold every active
|
|
98
|
+
schedule. E-TOU-D additionally selects weekday versus weekend/observed-holiday
|
|
99
|
+
periods from the vendored tariff calendar.
|
|
98
100
|
|
|
99
101
|
Nothing here touches the network at runtime.
|
|
100
102
|
|
|
@@ -102,18 +104,20 @@ Nothing here touches the network at runtime.
|
|
|
102
104
|
|
|
103
105
|
| | |
|
|
104
106
|
|---|---|
|
|
105
|
-
| [Configuration](docs/configuration.md) | Settings, CCA setup, reading your bill |
|
|
106
|
-
| [Library](docs/library.md) | Embedding in Python |
|
|
107
|
-
| [Named account profiles](docs/accounts.md) | Tracking a changing service agreement over time, importing PG&E statements |
|
|
108
|
-
| [Bill calculator](docs/billing.md) | Computing a cycle from interval meter data |
|
|
109
|
-
| [MQTT](docs/mqtt.md) | Publishing, with Home Assistant discovery |
|
|
110
|
-
| [REST API](docs/web.md) | HTTP service |
|
|
111
|
-
| [Home Assistant](docs/home-assistant.md) | Custom component, Energy dashboard, account history, response actions, opt-in Predbat |
|
|
112
|
-
| [
|
|
113
|
-
| [
|
|
114
|
-
| [
|
|
115
|
-
| [
|
|
116
|
-
| [
|
|
107
|
+
| [Configuration](https://github.com/eman/tariffkit/blob/main/docs/configuration.md) | Settings, CCA setup, reading your bill |
|
|
108
|
+
| [Library](https://github.com/eman/tariffkit/blob/main/docs/library.md) | Embedding in Python |
|
|
109
|
+
| [Named account profiles](https://github.com/eman/tariffkit/blob/main/docs/accounts.md) | Tracking a changing service agreement over time, importing PG&E statements |
|
|
110
|
+
| [Bill calculator](https://github.com/eman/tariffkit/blob/main/docs/billing.md) | Computing a cycle from interval meter data |
|
|
111
|
+
| [MQTT](https://github.com/eman/tariffkit/blob/main/docs/mqtt.md) | Publishing, with Home Assistant discovery |
|
|
112
|
+
| [REST API](https://github.com/eman/tariffkit/blob/main/docs/web.md) | HTTP service |
|
|
113
|
+
| [Home Assistant](https://github.com/eman/tariffkit/blob/main/docs/home-assistant.md) | Custom component, Energy dashboard, stacked component charts, account history, response actions, opt-in Predbat |
|
|
114
|
+
| [Predbat](https://github.com/eman/tariffkit/blob/main/docs/predbat.md) | Installing TariffKit and Predbat together, end to end |
|
|
115
|
+
| [Predbat on Sigenergy](https://github.com/eman/tariffkit/blob/main/docs/predbat-sigenergy.md) | Sigenergy SigenStor specifics: entity mapping, sign and unit conversions, control |
|
|
116
|
+
| [Home Assistant quality checklist](https://github.com/eman/tariffkit/blob/main/docs/home-assistant-quality.md) | Self-assessment against the Integration Quality Scale, with exemptions |
|
|
117
|
+
| [Containers](https://github.com/eman/tariffkit/blob/main/docs/containers.md) | Local Home Assistant development stack and API/MQTT deployment proposal |
|
|
118
|
+
| [Maintaining rate data](https://github.com/eman/tariffkit/blob/main/docs/data.md) | Regenerating export rates, updating the retail tariff and CCA cards |
|
|
119
|
+
| [Packaging strategy](https://github.com/eman/tariffkit/blob/main/docs/packaging_strategy.md) | Architecture decision, boundaries, and release model |
|
|
120
|
+
| [Release procedure](https://github.com/eman/tariffkit/blob/main/docs/releases.md) | Versioning, Trusted Publishing, verification, and recovery |
|
|
117
121
|
|
|
118
122
|
## Works with
|
|
119
123
|
|
|
@@ -129,8 +133,8 @@ Predbat opt-in.
|
|
|
129
133
|
| **EMHASS** | `tariffkit.get_emhass_forecast` action, called with any window | `load_cost_forecast` / `prod_price_forecast` attributes, always published |
|
|
130
134
|
| **Predbat** | `raw_today` / `raw_tomorrow` attributes, only once enabled in options | `raw_today` / `raw_tomorrow` attributes, always published |
|
|
131
135
|
|
|
132
|
-
See [docs/home-assistant.md](docs/home-assistant.md) and
|
|
133
|
-
[docs/mqtt.md](docs/mqtt.md) for setup of each.
|
|
136
|
+
See [docs/home-assistant.md](https://github.com/eman/tariffkit/blob/main/docs/home-assistant.md) and
|
|
137
|
+
[docs/mqtt.md](https://github.com/eman/tariffkit/blob/main/docs/mqtt.md) for setup of each.
|
|
134
138
|
|
|
135
139
|
## Install
|
|
136
140
|
|
|
@@ -190,7 +194,7 @@ base_services_charge_tier = 3
|
|
|
190
194
|
`TARIFFKIT_*` environment variables override any file setting.
|
|
191
195
|
Long-lived PG&E, Home Assistant, InfluxDB, and MQTT credentials can be stored
|
|
192
196
|
outside that file with `tariffkit credentials set`; see
|
|
193
|
-
[Configuration](docs/configuration.md#credentials).
|
|
197
|
+
[Configuration](https://github.com/eman/tariffkit/blob/main/docs/configuration.md#credentials).
|
|
194
198
|
|
|
195
199
|
### What the numbers include
|
|
196
200
|
|
|
@@ -245,7 +249,7 @@ franchise_fee_surcharge = 0.00042 # $/kWh, from your bill
|
|
|
245
249
|
```
|
|
246
250
|
|
|
247
251
|
For other CCAs, supply `generation_rates` directly; see
|
|
248
|
-
[docs/configuration.md](docs/configuration.md). Until generation rates and a
|
|
252
|
+
[docs/configuration.md](https://github.com/eman/tariffkit/blob/main/docs/configuration.md). Until generation rates and a
|
|
249
253
|
franchise fee are configured, CCA mode returns delivery-only prices flagged
|
|
250
254
|
`complete = False` rather than quietly understating your rates.
|
|
251
255
|
|
|
@@ -274,11 +278,11 @@ rather than as silent drift. Export files are updated by **October 1** of any
|
|
|
274
278
|
year the CPUC adopts a new Avoided Cost Calculator; retail rates change more
|
|
275
279
|
often, via advice letters — three times in the first half of 2026 alone — which
|
|
276
280
|
is why a superseded vintage can be rebuilt from the filing that adopted it. See
|
|
277
|
-
[docs/data.md](docs/data.md).
|
|
281
|
+
[docs/data.md](https://github.com/eman/tariffkit/blob/main/docs/data.md).
|
|
278
282
|
|
|
279
283
|
## Data sources
|
|
280
284
|
|
|
281
|
-
See [docs/data.md](docs/data.md#data-sources) for the source of every vendored
|
|
285
|
+
See [docs/data.md](https://github.com/eman/tariffkit/blob/main/docs/data.md#data-sources) for the source of every vendored
|
|
282
286
|
table and why OpenEI's URDB is deliberately not used.
|
|
283
287
|
|
|
284
288
|
## License
|