M8-T16: add thermal meter cost APIs

This commit is contained in:
2026-08-23 21:22:06 +02:00
parent 489e5b596a
commit 3eec701448
12 changed files with 1843 additions and 19 deletions
+615
View File
@@ -704,6 +704,20 @@
"description": "Return the price curve for the active contract.\n\n**Tibber contracts** (kind=\"tibber\"):\n Fetches ``tibber_price`` rows within ``[start, end]``, ordered ascending\n by ``starts_at``. At most ``limit`` rows are returned (most recent first\n within the window, then reversed to ascending order — identical to the\n modbus readings pattern).\n\n Response ``points`` carries per-slot:\n - ``buy = total`` (Tibber all-inclusive price)\n - ``sell = total energy_tax sell_fee sell_adjust`` (from active version values)\n - ``level`` (Tibber price level, may be null)\n\n ``tariff`` is null.\n\n**Manual contracts** (kind=\"manual\"):\n ``points`` is empty. ``tariff`` carries the four effective prices\n derived using the billing engine formula:\n - ``buy_dal = energy.buy.dal + energy_tax + ode``\n - ``buy_normal = energy.buy.normal + energy_tax + ode``\n - ``sell_dal = energy.sell.dal``\n - ``sell_normal = energy.sell.normal``\n\n**No active contract**: returns kind=null, currency=\"EUR\", points=[], tariff=null (200).",
"operationId": "get_prices_api_energy_prices_get",
"parameters": [
{
"name": "scope",
"in": "query",
"required": false,
"schema": {
"enum": [
"electricity",
"thermal"
],
"type": "string",
"default": "electricity",
"title": "Scope"
}
},
{
"name": "start",
"in": "query",
@@ -1401,6 +1415,288 @@
}
}
},
"/api/energy/meter-costs": {
"get": {
"tags": [
"api-energy"
],
"summary": "Get Meter Costs",
"description": "List thermal rows in a half-open time window with stable pagination.",
"operationId": "get_meter_costs_api_energy_meter_costs_get",
"parameters": [
{
"name": "scope",
"in": "query",
"required": false,
"schema": {
"const": "thermal",
"type": "string",
"default": "thermal",
"title": "Scope"
}
},
{
"name": "commodity",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"enum": [
"heating",
"hot_water"
],
"type": "string"
},
{
"type": "null"
}
],
"title": "Commodity"
}
},
{
"name": "start",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "Start"
}
},
{
"name": "end",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "End"
}
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"maximum": 5000,
"minimum": 1,
"default": 500,
"title": "Limit"
}
},
{
"name": "offset",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 0,
"default": 0,
"title": "Offset"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MeterCostsResponse"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/energy/meter-costs/summary": {
"get": {
"tags": [
"api-energy"
],
"summary": "Get Meter Cost Summary",
"description": "Summarize thermal variable and once-per-contract daily fixed costs.",
"operationId": "get_meter_cost_summary_api_energy_meter_costs_summary_get",
"parameters": [
{
"name": "scope",
"in": "query",
"required": false,
"schema": {
"const": "thermal",
"type": "string",
"default": "thermal",
"title": "Scope"
}
},
{
"name": "start",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "Start"
}
},
{
"name": "end",
"in": "query",
"required": false,
"schema": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "End"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ThermalCostSummaryResponse"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/energy/meter-costs/recompute": {
"post": {
"tags": [
"api-energy"
],
"summary": "Post Meter Cost Recompute",
"description": "Atomically overwrite closed, UTC-quarter thermal rows in a bounded window.",
"operationId": "post_meter_cost_recompute_api_energy_meter_costs_recompute_post",
"parameters": [
{
"name": "scope",
"in": "query",
"required": false,
"schema": {
"const": "thermal",
"type": "string",
"default": "thermal",
"title": "Scope"
}
},
{
"name": "start",
"in": "query",
"required": true,
"schema": {
"type": "string",
"format": "date-time",
"title": "Start"
}
},
{
"name": "end",
"in": "query",
"required": true,
"schema": {
"type": "string",
"format": "date-time",
"title": "End"
}
},
{
"name": "X-CSRF-Token",
"in": "header",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "X-Csrf-Token"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MeterCostRecomputeResponse"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/energy/meters": {
"get": {
"tags": [
@@ -4611,6 +4907,173 @@
"title": "MeterBindingSummary",
"description": "Stable, non-sensitive binding identity embedded in meter responses."
},
"MeterCostPeriodSchema": {
"properties": {
"commodity": {
"type": "string",
"enum": [
"heating",
"hot_water"
],
"title": "Commodity"
},
"period_start": {
"type": "string",
"format": "date-time",
"title": "Period Start"
},
"period_end": {
"type": "string",
"format": "date-time",
"title": "Period End"
},
"meter_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Meter Id"
},
"source_binding_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Source Binding Id"
},
"contract_version_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Contract Version Id"
},
"quantity": {
"type": "string",
"title": "Quantity"
},
"cost": {
"type": "string",
"title": "Cost"
},
"currency": {
"type": "string",
"title": "Currency"
},
"cost_breakdown": {
"additionalProperties": {
"type": "string"
},
"type": "object",
"title": "Cost Breakdown"
},
"pricing_snapshot": {
"additionalProperties": {
"additionalProperties": {
"type": "string"
},
"type": "object"
},
"type": "object",
"title": "Pricing Snapshot"
},
"quality": {
"type": "string",
"title": "Quality"
},
"degraded": {
"type": "boolean",
"title": "Degraded"
},
"degraded_reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Degraded Reason"
}
},
"type": "object",
"required": [
"commodity",
"period_start",
"period_end",
"meter_id",
"source_binding_id",
"contract_version_id",
"quantity",
"cost",
"currency",
"cost_breakdown",
"pricing_snapshot",
"quality",
"degraded",
"degraded_reason"
],
"title": "MeterCostPeriodSchema",
"description": "One auditable thermal ledger row; all Decimal values are JSON strings."
},
"MeterCostRecomputeResponse": {
"properties": {
"processed": {
"type": "integer",
"title": "Processed"
},
"normal": {
"type": "integer",
"title": "Normal"
},
"degraded": {
"type": "integer",
"title": "Degraded"
}
},
"type": "object",
"required": [
"processed",
"normal",
"degraded"
],
"title": "MeterCostRecomputeResponse"
},
"MeterCostsResponse": {
"properties": {
"items": {
"items": {
"$ref": "#/components/schemas/MeterCostPeriodSchema"
},
"type": "array",
"title": "Items"
},
"total": {
"type": "integer",
"title": "Total",
"description": "Total matching rows before pagination."
}
},
"type": "object",
"required": [
"items",
"total"
],
"title": "MeterCostsResponse"
},
"MeterDeclareRequest": {
"properties": {
"label": {
@@ -5830,6 +6293,62 @@
}
],
"description": "Fixed tariff table for manual contracts. Null for tibber contracts and when no active contract exists."
},
"contract_version_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Contract Version Id",
"description": "Thermal active contract version identifier; omitted for electricity."
},
"effective_from": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "Effective From",
"description": "Thermal contract version start; omitted for electricity."
},
"effective_to": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "Effective To",
"description": "Thermal contract version end; omitted for electricity."
},
"values": {
"anyOf": [
{
"additionalProperties": {
"additionalProperties": {
"type": "string"
},
"type": "object"
},
"type": "object"
},
{
"type": "null"
}
],
"title": "Values",
"description": "Thermal normalized Decimal-string contract values; omitted for electricity."
}
},
"type": "object",
@@ -6336,6 +6855,102 @@
"title": "SummaryResponse",
"description": "Response for GET /api/energy/costs/summary.\n\nMonetary values are in ``currency``; the ``*_kwh`` fields are energy totals\nin kWh. ``metered_import``/``metered_export`` are **money**, not energy —\nonly the ``_kwh``-suffixed fields carry kWh.\n\n``total_payable = metered_net + fixed_costs credits``"
},
"ThermalCostSummaryResponse": {
"properties": {
"currency": {
"type": "string",
"title": "Currency"
},
"heating": {
"type": "string",
"title": "Heating"
},
"hot_water_heating": {
"type": "string",
"title": "Hot Water Heating"
},
"hot_water": {
"type": "string",
"title": "Hot Water"
},
"hot_water_tax": {
"type": "string",
"title": "Hot Water Tax"
},
"variable_subtotal": {
"type": "string",
"title": "Variable Subtotal"
},
"fixed_breakdown": {
"$ref": "#/components/schemas/ThermalFixedBreakdown"
},
"fixed_subtotal": {
"type": "string",
"title": "Fixed Subtotal"
},
"all_in": {
"type": "string",
"title": "All In"
},
"period_count": {
"type": "integer",
"title": "Period Count"
},
"degraded_count": {
"type": "integer",
"title": "Degraded Count"
}
},
"type": "object",
"required": [
"currency",
"heating",
"hot_water_heating",
"hot_water",
"hot_water_tax",
"variable_subtotal",
"fixed_breakdown",
"fixed_subtotal",
"all_in",
"period_count",
"degraded_count"
],
"title": "ThermalCostSummaryResponse"
},
"ThermalFixedBreakdown": {
"properties": {
"heating_network": {
"type": "string",
"title": "Heating Network"
},
"metering": {
"type": "string",
"title": "Metering"
},
"delivery_set": {
"type": "string",
"title": "Delivery Set"
},
"hot_water_network": {
"type": "string",
"title": "Hot Water Network"
},
"other": {
"type": "string",
"title": "Other"
}
},
"type": "object",
"required": [
"heating_network",
"metering",
"delivery_set",
"hot_water_network",
"other"
],
"title": "ThermalFixedBreakdown",
"description": "D11 annual-standing charges accrued per settled local day, as Decimal strings."
},
"TibberTestPriceSchema": {
"properties": {
"starts_at": {