Files
home-automation/docs/references/Tibber-and-NL-Energy-Pricing.md
tliu93 d07a083e03
frontend / frontend (push) Failing after 5m49s
pytest / test (push) Successful in 20m30s
docker-image / build-and-push (push) Successful in 13m26s
fix(tibber): deduct verkoopvergoeding (sell_fee) from feed-in sell price
Tibber's API `total` already includes the buy-side inkoopvergoeding
(verified from production data: total = spot×1.21 + energy_tax 0.11085 +
inkoopvergoeding 0.0248). Under net metering Tibber pays back
`total − verkoopvergoeding` per returned kWh (NL: EUR 0.28 -> 0.2552), so the
two EUR 0.0248 fees do NOT cancel — the feed-in price sits 0.0248 below buy.

Model the verkoopvergoeding as a first-class, always-subtracted contract
field `energy.sell_fee` (default 0.0248) instead of folding it into
`sell_adjust`. New sell formula:

    sell = total − energy_tax − sell_fee − sell_adjust

`sell_adjust` now carries only the net-metering energy-tax refund
(= −energy_tax). Applied in both the billing strategy and the /prices
endpoint; recorded in the pricing snapshot. Frontend renders the field
automatically (dynamic profile form). Docs (references, m6) corrected to
drop the wrong "fees cancel" premise.
2026-07-20 13:50:13 +02:00

17 KiB
Raw Permalink Blame History

Tibber API + 荷兰电价 + DSMR Reader 参考

本文件汇总 M6(动态/固定电价 → 实时买卖电费计算)所需的全部外部事实,供 docs/design/m6-tibber-dynamic-energy.md 与实现者直接 refer。 来源:Tibber GraphQL introspection(实证)、Tibber Explorer demo 数据、Tibber NL support 文档、用户实际合同与 DSMR Reader 配置(2026-06 整理)。 凡标「 实证」的为代码/数据验证过的事实;金额类以用户真实账单 / 合同生效后真实 token 为最终准(见末尾「待真实数据核对」)。


1. Tibber GraphQL API

  • EndpointPOST https://api.tibber.com/v1-beta/gql
  • 认证HTTP header Authorization: Bearer <token>token 在 developer.tibber.com 登录后生成(每个 Tibber 账户一个)。
  • 在线 Explorerdeveloper.tibber.com/explorer,自带一个公共 demo token
    • ⚠️ demo token 现在只能做 schema introspection 与历史 demo 数据查询;对 viewer 的真实数据查询返回 UNAUTHENTICATED。要拿自己家的实时价,必须用自己账户的 token

1.1 Price 类型( introspection 实证)

Price 对象恰好 6 个字段:

字段 类型 含义
total Float 全包价 = energy + tax(即 App 里的 All-in Price,含 spot+能源税+VAT 等)
energy Float 纯能源/现货分量(ex-VAT 市场价)
tax Float 税费分量(能源税 + VAT 合并)
startsAt String 该价格段起点(ISO8601含时区偏移,如 +02:00
currency String! 币种(唯一 non-null 字段;NL 为 EURdemo 为 SEK
level PriceLevel 价格档枚举(VERY_CHEAP/CHEAP/NORMAL/EXPENSIVE/VERY_EXPENSIVE

实证total == energy + tax 精确成立(见 §1.4 样本)。

1.2 15 分钟价格:Subscription.priceInfoRange introspection 实证)

  • 字段currentSubscription.priceInfoRange
  • 参数resolution: PriceInfoRangeResolution!(必填)、first: Intlast: Intbefore: Stringafter: String(游标分页)
  • resolution 枚举 PriceInfoRangeResolution:值 DAILY / HOURLY / QUARTER_HOURLY
    • ⚠️ 注意别用错枚举:另有 PriceResolution(只有 HOURLY/DAILY无 QUARTER_HOURLY)和 PriceInfoResolution——priceInfoRange 用的是 PriceInfoRangeResolution
  • 返回SubscriptionPriceConnection { nodes: [Price]!, edges: [...], pageInfo }
  • 废弃说明:老的 priceInfo.rangedeprecationReason: "use Subscription.priceInfoRange instead"不支持 QUARTER_HOURLY,必须改用 Subscription.priceInfoRange
  • 实证(demo key2018 历史日)QUARTER_HOURLY 返回 96 个节点、间隔精确 15 分钟;无真 15 分钟价的历史日,API 把小时价重复成 4 个相同刻钟00/15/30/45 同值,到下一整点才变);NL 当前数据则是真 15 分钟。
  • 解析注意不要假设固定 96 个节点 / 固定 15 分钟间隔,按 startsAt 逐点存最稳。

15 分钟查询(拉今天 96 个刻钟)

{ viewer { homes { id currentSubscription {
    priceInfoRange(resolution: QUARTER_HOURLY, first: 96) {
      nodes { startsAt total energy tax currency level }
} } } } }

1.3 小时价 / 当前价 / 探测:priceInfo

currentSubscription.priceInfocurrent(当前价)、today[]tomorrow[](次日价约 13:00 CET 发布)。探测账户能力 + 当前价:

{ viewer { homes {
    id appNickname
    features { realTimeConsumptionEnabled }
    currentSubscription { priceInfo {
      current  { total energy tax startsAt level currency }
      today    { total energy tax startsAt level }
      tomorrow { total energy tax startsAt level }
} } } } }

1.4 现成 curl + 样本响应

# 15 分钟价(换成自己账户 tokendemo token 拿不到 viewer 真实数据)
curl -s -X POST https://api.tibber.com/v1-beta/gql \
  -H "Authorization: Bearer <YOUR_TOKEN>" -H "Content-Type: application/json" \
  -d '{"query":"{ viewer { homes { id currentSubscription { priceInfoRange(resolution: QUARTER_HOURLY, first: 96) { nodes { startsAt total energy tax currency } } } } } }"}'

样本:priceInfodemo,瑞典家庭,SEK

{"current": {"total": 0.8239, "energy": 0.5663, "tax": 0.2576,
             "startsAt": "2026-06-23T14:00:00.000+02:00", "level": "NORMAL", "currency": "SEK"},
 "today":   [{"total": 1.276, "energy": 0.928, "tax": 0.348,
              "startsAt": "2026-06-23T00:00:00.000+02:00", "level": "VERY_EXPENSIVE"},  24 ],
 "tomorrow":[ 24 ]}

样本:priceInfoRange QUARTER_HOURLYdemo2018-11-0296 节点)

{"nodes": [
  {"startsAt":"2018-11-02T00:00:00.000+01:00","total":0.63,  "energy":0.435,"tax":0.195,"currency":"SEK"},
  {"startsAt":"2018-11-02T00:15:00.000+01:00","total":0.63,  "energy":0.435,"tax":0.195,"currency":"SEK"},
  {"startsAt":"2018-11-02T00:30:00.000+01:00","total":0.63,  "energy":0.435,"tax":0.195,"currency":"SEK"},
  {"startsAt":"2018-11-02T00:45:00.000+01:00","total":0.63,  "energy":0.435,"tax":0.195,"currency":"SEK"},
  {"startsAt":"2018-11-02T01:00:00.000+01:00","total":0.6141,"energy":0.4223,"tax":0.1918,"currency":"SEK"},
    96 个,末节点 23:45 total 0.6342]}

(注意 00:00–00:45 四个刻钟同值 = 小时价被重复;total==energy+tax。)


2. 荷兰电价构成(Tibber NL + 用户合同)

所有金额含 VATincl. btw,除非另注。VAT/BTW 标准税率 21%

分量 荷兰语 单位 值 / 说明
现货/市场价 marktprijs / dynamische kwartierprijs €/kWh 每 15 分钟跟随交易所;= API energy
买侧采购费 inkoopvergoeding €/kWh €0.0248(覆盖 onbalans + 绿证;见下「相等」事实)
卖侧上网费 verkoopvergoeding €/kWh €0.02482026-01-01 起)
能源税 energiebelasting €/kWh(含 VAT 2026 第一档(010000 kWh)≈ €0.11082025 ≈ €0.1228;高档更低
增值税 BTW % 21%
固定月费 vast bedrag / leveringskosten €/月/合同 Tibber €5.99/月(电、气各一次)
电网维护费 netbeheerkosten / systeembeheerkosten €/天或/月 电网公司定、供应商代收;按地区,2026 约 +3.38%
能源税抵扣 heffingskorting / vermindering energiebelasting €/年/连接 政府年度减免,从总费用扣
ODE Opslag Duurzame Energie €/kWh 现为 0(已并入 energiebelasting

关键事实(Tibber NL 文档原文)

"De verkoopvergoeding van 2,48 cent is gelijk aan de inkoopvergoeding die je bij je afgenomen stroom betaalt." (卖侧 verkoopvergoeding 2.48 分 = 买侧 inkoopvergoeding。)

买卖服务费金额相等(均 €0.0248/kWh),但两者对住户都是成本、不互相抵消

  • 买侧 inkoopvergoeding 已经包含在 Tibber API 的 total(见下 §3.1 的实证拆解),买电按 total 计价即已含它。
  • 卖侧 verkoopvergoeding 则是从回送价里额外扣掉的一笔——所以回送价 = total 0.0248,比买价低 0.0248/kWh。
  • ⚠️ 早期版本误判为"一进一出抵消 → 回送=total",这是错的:total 里那笔 inkoopvergoeding 不会退回来充抵 verkoopvergoeding。代码里用 energy.sell_fee(默认 0.0248)建模这笔卖侧费用。

3. 净计量(saldering)、回送(teruglevering)、负电价、2027

3.1 回送价(净计量期内,文档原文 + 实证)

"Op het moment dat je teruglevert geven we je per kWh de beursprijs die op dat moment geldt …, inclusief energiebelasting en inkoopvergoeding plus de btw minus de verkoopvergoeding."

Worked exampleTibber NL 原文)"Stel dat tussen 14:00 en 14:15 de totale stroomprijs €0,28 per kWh incl. is, dan krijg je €0,28 €0,0248 verkoopvergoeding = €0,2552 per teruggeleverde kWh terug."

即净计量期内回送价 = beursprijs + energiebelasting + inkoopvergoeding + btw verkoopvergoeding,而官方例子直接写成 回送价 = totale stroomprijs verkoopvergoeding = total 0.0248。能源税退回(留在 total 里没动),只有 verkoopvergoeding 这 0.0248 被扣。

实证(本项目生产库,2026-07-20 三个刻钟):按 21% VAT 拆 totaltotal = 现货×1.21 + energiebelasting(0.11085) + inkoopvergoeding(0.0248),三段解出的 inkoop 都精确等于 0.0248。→ 我们存的 tibber_price.total 就是官方 "totale stroomprijs"(含 inkoopvergoeding 的买价),因此:

  • 买价 buy = total(已含 inkoopvergoeding,正确)。
  • 净计量回送价 sell = total verkoopvergoeding = total 0.0248
  • ⚠️ 所以 saldering 下"回送 1 度"仍比"用 1 度"少 0.0248——不是完全 1:1。代码用 sell_fee 建模这笔扣减,sell_adjust 只负责在净计量期把能源税补回(sell_adjust = energy_tax)。

3.2 年末盈余 / 取消净计量后(文档原文,Scenario 2)

"Voor de overproductie van 500 kWh heb je recht op de beursprijs en de inkoopvergoeding, maar heb je geen recht op de energiebelasting. … ontvang je nog een factuur van ons voor de te veel uitgekeerde belastingen …"

超额回送(或 2027 取消净计量后):盈余按 beursprijs + inkoopvergoeding verkoopvergoeding无能源税)计价 → 因两费抵消 → = 纯 spot

3.3 净计量机制

  • 法定按自然年结算;可抵扣到「用电量」为止(回送抵到用电为止)。
  • Tibber 不用预付,净计量周期在第 12 张账单后结束
  • 2027 起荷兰取消净计量(saldering stopt in 2027——用户据此设计:不再考虑净计量,直接按实时买卖算

3.4 负电价

  • 负价时用电:理论上你拿钱(但仍计能源税,净计量期内税会退回)。
  • 负价时回送:你要为回送的电付那个负价(= 倒贴)。
  • 因 Tibber 给的是 all-in total(已含能源税),只有 spot 负到比能源税还多,total 才转负;中等负价时 total 仍正(照付)。
  • 结论:负电价不需要特殊处理——buy=totalsell=total−能源税 符号自洽。

4. 本项目采用的买/卖价公式(M6

spot 取 API energytotal = energy + tax(全包)。买价直接用 total,卖价从 total 扣掉卖电不交的能源税。

  • Tibber 动态合同
    • 买价 buy = price.total(含 energy_tax + VAT + inkoopvergoeding
    • 卖价 sell = price.total energy_tax sell_fee sell_adjust
      • sell_feeverkoopvergoeding(卖侧上网费,默认 0.0248,含 VAT),始终扣除——即使净计量期也扣(见 §3.1)。
      • sell_adjust:手动修正项(默认 0)。净计量期设为 energy_tax(把能源税补回),得 sell = total sell_fee2027 取消净计量后设为 0,得 sell = total energy_tax sell_fee(无能源税、纯市场价再扣上网费)。
  • 固定合同(manual,双费率)
    • 买价 buy_档 = energy_buy_档 + energy_tax(档 ∈ {normal, dal}
    • 卖价 sell_档 = sell_档(回送价,无能源税
  • 固定费/抵扣不进每度network_feemanagement_fee(月→天)、heffingskorting(年→天)在日/月/年汇总层加减。
  • 详见 docs/design/m6-tibber-dynamic-energy.md §3.4。

5. 用户当前固定合同(manual 实例,2026-06

双费率(NL_1=dal/低谷、_2=normal/高峰)。以下为当前值,会随合同阶段/年度变(系统按"加新版本+生效日期"留底)。

当前值 备注
能源价 normal(高) €0.133/kWh 买侧,含 VAT,不含能源税
能源价 dal(低) €0.127/kWh 买侧
回送价 normal (现与 dal 同) 卖侧,分档但现同价;系统仍分开填
回送价 dal 卖侧
能源税 energiebelasting 待填(≈€0.1108 含 VAT,加到买价
ODE 0 已并入能源税
电网维护费 待填 按天(合同按天收)
供应商管理费 ≈€0.329/天(≈€9.87/月) 对应 Tibber 的 €5.99/月
heffingskorting 待填 年度减免,汇总时扣
回送阶梯罚金 terugleverkosten 不做 按自然年累计、阶梯式;用户住不到年底算不准,M6 不实现

6. DSMR Reader → MQTT

6.1 取数方式

  • Telegram JSON(单 topic dsmr/json,一条 = 一帧完整 telegram 的 JSON),不用 split-topic(每字段一 topic、要按 id 拼)、不用 raw(未解析 OBIS)。
  • DSMR Reader 每秒一条。本项目按 10 秒降采样(仅秒数整 10 落盘)、整帧存 JSON blob(不做字段 allowlist)。

6.2 telegram JSON 字段(DSMR Reader 配置的 JSON mapping

id, timestamp,
electricity_delivered_1, electricity_delivered_2,        # 进口累计 kWh_1=dal 低谷, _2=normal 高峰)
electricity_returned_1,  electricity_returned_2,         # 出口/回送累计 kWh
electricity_currently_delivered, electricity_currently_returned,   # 瞬时进/出口功率 kW
phase_currently_delivered_l1/l2/l3, phase_currently_returned_l1/l2/l3,  # 各相瞬时功率 kW
phase_voltage_l1/l2/l3,                                  # 各相电压 V
phase_power_current_l1/l2/l3,                            # 各相电流 A
extra_device_timestamp, extra_device_delivered           # 燃气表(m³,每 5 分钟更新)

6.3 实测样本(单相,dsmr/json

{
  "id": 200086230,
  "timestamp": "2026-06-23T12:16:48Z",
  "electricity_delivered_1": "20915.154",
  "electricity_returned_1":  "2979.905",
  "electricity_delivered_2": "15212.090",
  "electricity_returned_2":  "6786.406",
  "electricity_currently_delivered": "0.000",
  "electricity_currently_returned":  "2.704",
  "phase_currently_delivered_l1": "0.000",
  "phase_currently_delivered_l2": null,
  "phase_currently_delivered_l3": null,
  "extra_device_timestamp": "2026-06-23T12:15:00Z",
  "extra_device_delivered": "6208.234",
  "phase_currently_returned_l1": "2.704",
  "phase_currently_returned_l2": null,
  "phase_currently_returned_l3": null,
  "phase_voltage_l1": "237.0",
  "phase_voltage_l2": null,
  "phase_voltage_l3": null
}

6.4 解析要点(实测确认)

  • 数值都是 JSON 字符串"20915.154""0.000")→ 计费读取转 Decimal(累计寄存器算钱要精度)。
  • 缺测相位 = null(不是缺 key)→ 视为缺测;单相只有 *_l1,三相后 _l2/_l3 由 null 变数字。
  • 进口总量 = electricity_delivered_1 + _2出口总量 = electricity_returned_1 + _2(双费率档对动态合同无意义,求和;对固定合同分档计价)。
  • timestamp 为 UTCZid 自增,做幂等去重。
  • 历史样本里电压 key 曾被错配成带前缀 dsmr/reading/phase_voltage_l1DSMR Reader 的 JSON mapping 配置笔误,用户已改对)——解析仍按"键名容错"。
  • 整表寄存器与相数无关delivered/returned_1/2 是整表总量,三相只多了各相瞬时通道 → 计费逻辑相数无关
  • 燃气 extra_device_delivered(m³)一并存入 blob;燃气计费 M6 不做(数据先留,未来按 commodity 扩展)。

7. 来源 URL

  • Tibber GraphQL reference / explorerhttps://developer.tibber.com/docs/reference#rootsubscriptionhttps://developer.tibber.com/explorer
  • Tibber NL 费用构成:https://support.tibber.com/nl/articles/5605892-de-kosten-bij-tibber
  • Tibber NL 净计量与回送:https://support.tibber.com/nl/articles/4669873-salderen-en-terugleveren-bij-tibber
  • DSMR ReaderHA 集成):https://www.home-assistant.io/integrations/dsmr_reader/

8. 待真实数据核对(合同生效后用真实 token / 账单)

  1. 真实 token 复核:跑 §1.4 的 15 分钟 curl,确认 NL 返回 15 分钟价(非重复小时价)+ 币种 EUR。
  2. 卖价残差:确认 total 里 purchase fee 是否被卖侧 sales fee 完全抵掉已核实(2026-07total 含 inkoopvergoeding0.0248),净计量回送价 = total verkoopvergoeding(0.0248),两费不抵消;代码以 sell_fee(默认 0.0248)建模。仍待真实账单核对 sell_fee / VAT 口径的最终残差。
  3. 双费率寄存器映射:确认 _1=dal/_2=normal 没接反(差价小但要对)。
  4. 能源税年值:按当年实际值与年用电档位核 energy_tax
  5. 固定合同数值:回送两档价、电网费、heffingskorting 待用户从账单填。