Download the PHP package supla/energy-cost-calculator without Composer
On this page you can find all versions of the php package supla/energy-cost-calculator. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download supla/energy-cost-calculator
More information about supla/energy-cost-calculator
Files in supla/energy-cost-calculator
Package energy-cost-calculator
Short Description Framework-agnostic PHP library for calculating electricity costs from interval energy deltas and JSON billing rules.
License GPL-2.0-or-later
Informations about the package energy-cost-calculator
supla/energy-cost-calculator
Framework-agnostic PHP 8.2+ library for calculating electricity-cost simulations from interval meter deltas and JSON billing rules.
The main design goal is to keep calculation logic independent from supla-cloud, Doctrine and the physical database schema. supla-cloud provides small adapters implementing two ports:
EnergyDeltaSourceReferenceDataSource
This makes the same Composer package usable today inside supla-cloud and later inside a standalone HTTP service.
Install during development
Add the package repository/path as usual and then:
Minimal usage
Requested range vs billing cycle
The requested calculation range and the billing cycle are independent concepts. Callers may request an hour, week, month, custom range, or a complete billing period. If a component uses temporal netting, the requested range must contain complete netting windows for that component.
A billing definition may declare an anchor and cycle length:
This produces cycles such as 15 Jan -> 15 Feb, 15 Feb -> 15 Mar, etc. If billingCycle is omitted, the default is a natural one-month cycle.
If invoice boundaries change over time, use billingCycles[] instead of billingCycle:
A validity boundary cuts the nominal cycle. In the example above, 15 Jun -> 15 Jul becomes a transitional 15 Jun -> 1 Jul period, followed by 1 Jul -> 1 Aug. There is no gap or overlap. prorate: true is calculated against the nominal, uncut period.
The result exposes billingPeriods[] summaries with usage, usage-based costs, periodic costs and totals for every effective billing period. Usage-based costs also expose byZone, both globally and per billing-period summary.
Usage-based costs are calculated from their natural charge windows. Periodic charges are deliberately kept out of time-series charge facts. Use charges[] for cost charts: ordinary components produce charges at meter-delta resolution, temporally netted components without allocation produce one charge per complete netting window, and allocated netting components produce one charge per allocation slot. intervals[] remains a meter-interval diagnostic view and never receives an artificial share of a wider netting-window cost.
When the requested range covers complete billing cycles, periodic charges are also calculated and costs.total contains the full amount. When the range covers only part of a billing cycle and periodic charges exist, costs.periodic.total and costs.total are null; periodicCharges[] still contains the fee definitions so the UI can display e.g. + 12 PLN/month.
With includeIntervals, the result also returns charges[]. Each charge has its natural [from,to) window, resolved quantity, selector result, rate and cost. intervals[] still contains raw meter usage and costs that are genuinely resolvable at that meter interval; a 60-minute netted component is intentionally absent from the four underlying 15-minute interval costs. The top-level usage is always the sum of the returned meter deltas.
JSON model
The top-level periods[] model allows rules for one meter to change over time without modifying historical measurements.
A component is defined by three independent concerns:
- quantity — what is charged and, optionally, how meter deltas are netted in time,
- selector — which zone/rule applies at the timestamp,
- rate — the actual rate, possibly from an external time series.
Temporal netting is declared directly on the quantity:
Supported strategies are IMPORT_MINUS_EXPORT and IMPORT_MINUS_EXPORT_CAP_ZERO. Without strategy, ACTIVE_ENERGY_IMPORT keeps the existing forward/import behavior. Without an allocation rule, a netting window must be complete and its selector result and rate must remain constant for the whole window.
Contracts that explicitly redistribute a wider net quantity may add a named allocation rule:
EQUAL splits the resolved 60-minute quantity equally into complete 15-minute pricing slots. Selector/rate stability is then required per allocated slot, and charges[] contains one fact per slot. intervals[] remains tied to raw meter deltas and never receives an allocated share.
A REFERENCE rate may declare sourceUnit as a runtime assertion and optional sourceMin/sourceMax bounds. The bounds clamp the raw source value before multiplier and add; they do not cap a billing-period weighted-average/effective price.
Example: dynamic energy (Fixing1) plus dynamic network zones (PDGSZ) plus a monthly fee:
See examples/definitions/ and schema/billing-definition.schema.json.
Tariff presets
resources/tariff-presets/ contains component presets. Polish OSD tariffs expose distribution components, seller tariffs/offers expose supply components, and generic presets provide user-configurable building blocks. Catalogue metadata includes concrete components (kind, componentId, label) so a host can render one compatible selector per cost component without hard-coding component IDs.
CostPlanStarterCatalog provides the simple setup path: starters such as TAURON Dystrybucja - G11 return only the matching default component recipe. They do not define billing cycles or period dates. The host inserts CostPlanStarter::components into a user-owned CostPlan period; for a first and only period, omitting validFrom/validTo makes it apply to the whole meter history. Persisted plans keep component preset IDs and explicit user overrides, not the starter ID.
Use the production-facing catalogue API instead of resolving package paths directly:
presets() returns catalogue metadata with a revision and without internal resource paths. get() rejects unknown identifiers and resources outside the bundled preset directory. A preset ID is a stable reference to one semantic tariff, offer, or generic definition; compatible corrections may change its revision without changing its ID. A real tariff/offer change or incompatible generic contract must use a new preset ID.
Cost plans use the component-based version 2 format described in docs/cost-plans.md and schema/cost-plan-v2.schema.json. Each effective period selects individual CostComponentKind values; billingCycles are configured separately.
Plan periods are contiguous and ordered. The first may have an open validFrom, the last may have an open validTo, and a single period may leave both boundaries open.
Preset validity dates describe the bundled tariff/offer edition; the cost-plan period determines when a user applies the selected component.
Use TariffPresetCompiler when compiling one preset and CostPlanCompiler for persisted user plans:
The cost-plan JSON stores stable preset IDs and user values/overrides, not a copied executable definition. Compiling later uses the current document for the same preset ID, so package-owned corrections automatically apply to existing plans. Omit preset-default values from values unless the user explicitly overrides them; this preserves inheritance of corrected defaults. kind is a compatibility role and componentId is the per-period identity, so repeated kinds require distinct IDs; inline periodic components may omit it to retain their legacy kind-derived ID. See schema/cost-plan-v2.schema.json and docs/cost-plans.md.
Bundled presets are complete defaults: TariffPresetCompiler can compile them with no input values. Every declared input targets a default template value and callers may override any of them when creating a plan. Billing-cycle settings belong to the cost plan; a preset's internal cycle exists only so that the preset can also compile independently.
Standard supply presets preserve the provenance of the energy-price defaults originally bundled with the OSD examples. Named dynamic offers are separate OFFER presets and may expose several components, for example ENERGY_PURCHASE plus SUPPLIER_FIXED. The package also provides generic constant and market-reference energy presets. See docs/component-presets-and-starters.md.
The current Polish OSD presets still cover variable distribution rather than a complete regulated invoice. Additional fixed/statutory components can be added as the catalogue grows without changing the CostPlan format.
SUPLA integration
examples/supla-cloud/ contains adapter examples for the existing tables:
supla_em_delta_logsupla_energy_price_log
In the current issue-307 branch, raw energy is stored with precision 100000, and supla_em_delta_log.date is the end of the 15-minute interval. The adapter converts raw values to kWh and yields package-owned DTOs.
Reference IDs proposed by the examples:
PL.PSE.RCEPL.PSE.PDGSZPL.TGE.FIXING1PL.TGE.FIXING1_HOURLYPL.TGE.FIXING2PL.TGE.FIXING2_HOURLY
Performance characteristics
The engine preloads each referenced external series once per calculation range, so it does not query Fixing/PDGSZ per meter interval. Meter deltas remain streamable through iterable.
For a single meter, 15-minute intervals are a small workload (~35k rows/year). If fleet-wide historical reports later become expensive, cache/materialize calculated cost facts outside this package; keep the 15-minute delta table as the measurement source of truth.
Decimal arithmetic
The default NativeDecimalMath keeps the package dependency-free and is appropriate for simulations. The public DecimalMath port is deliberately injectable so invoice-grade deployments can replace it with an exact decimal implementation without changing the engine.
Tests
The starter tests cover:
- constant energy rate,
- periodic fee,
- Fixing1 reference rate,
- hourly import/export netting and hourly Fixing compatibility,
- rejection of rate changes inside a netting window,
- PDGSZ-based dynamic zone selection,
- billing rules changing over time.
Bundled public-holiday calendars
The package currently bundles the Polish statutory public-holiday calendar:
The source file is resources/calendars/PL.json and explicitly covers local dates from 2018-01-01 up to, but not including, 2031-01-01 (therefore through the end of 2030).
Dates are stored explicitly rather than generated algorithmically. This keeps historical calculations deterministic and allows legal exceptions to be represented directly. The bundled Polish data includes, among other dates:
- the one-off public holiday on 12 November 2018,
- movable Easter/Pentecost/Corpus Christi dates,
- Christmas Eve starting from 24 December 2025.
WEEKLY_SCHEDULE supports an optional calendar and the pseudo-day HOLIDAY:
Holiday rules are evaluated before ordinary weekday rules. A HOLIDAY rule requires calendar to be configured. HOLIDAY must not be mixed with weekday names in the same rule.
If a calculation requests a holiday date outside the bundled calendar coverage, the default provider throws HolidayCalendarCoverageException instead of silently treating the day as a non-holiday.
Applications that need calendars from another source can inject a custom HolidayCalendarProvider by constructing DefaultSelectorResolver with their provider and passing that resolver to CostCalculator.
See examples/definitions/weekly-schedule-polish-holidays.json.
Seasonal and overnight schedules
WEEKLY_SCHEDULE also supports the richer rule format carried over from the supla-cloud issue-307 tariff resolver:
- recurring
seasonsdefined with--MM-DDboundaries, - optional
seasonandpriorityper rule, - multiple
time_rangesper rule, - ranges crossing midnight, e.g.
22:00–06:00.
The original single from/to rule syntax remains supported for backwards compatibility. See docs/schedules.md for the full format and semantics.
The files in tests/Fixtures/Tariffs/ are regression fixtures for tariff structures. Their monetary rates are intentionally synthetic; they are not an official operator price catalogue.
All versions of energy-cost-calculator with dependencies
ext-json Version *