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": {
+404
View File
@@ -538,6 +538,16 @@ paths:
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
required: false
@@ -1092,6 +1102,180 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/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:
@@ -3434,6 +3618,120 @@ components:
- ended_at
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:
@@ -4310,6 +4608,37 @@ components:
- type: 'null'
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
required:
- kind
@@ -4710,6 +5039,81 @@ components:
``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: