Integrare custom pentru Home Assistant care importă prețurile energiei electrice de pe OPCOM România — Piața pentru Ziua Următoare (PZU/DAM).
Oferă senzori în timp real pentru prețul curent, ferestre optime de cumpărare/vânzare, și semnale binare pentru automatizarea încărcării/descărcării bateriilor (sisteme de stocare energie, vehicule electrice, pompe de căldură etc.).
- Descarcă automat prețurile PZU de pe OPCOM.ro la interval configurabil (implicit: 15 minute)
- Suportă 3 rezoluții: PT15M (96 intervale/zi), PT30M (48 intervale/zi), PT60M (24 intervale/zi)
- Calculează ferestrele optime de preț ieftin/scump — non-suprapuse (algoritm greedy)
- Senzori binari pentru automatizări: „Ar trebui să încarce acum?" / „Ar trebui să exporte acum?"
- Cross-day: senzorii binari văd și prețurile de mâine (când sunt disponibile) — util seara
- Filtrare viitor: senzorii de recomandare ignoră intervalele deja trecute
- Intervale rămase: câte sloturi „bune" mai rămân azi (pentru planificarea bateriei)
- Diagnostics complet: pagina de diagnostics expune toate datele raw pentru troubleshooting
Datele vin direct de pe OPCOM.ro, fișiere CSV publice:
https://www.opcom.ro/rapoarte-pzu-raportPIP-export-csv/{zi}/{luna}/{an}/{lang}?resolution={res}
OPCOM publică prețurile PZU (Piața pentru Ziua Următoare) în fiecare zi, de regulă între orele 13:00 și 15:00, pentru ziua următoare. Până la publicare, senzorii „mâine" afișează „Necunoscut" (Unknown) — comportament normal.
OPCOM operează pe CET (Central European Time = Europe/Berlin), nu pe ora României (EET = Europe/Bucharest). Asta e confirmat oficial de OPCOM:
„Toate aspectele legate de participarea pe PZU se raportează la ore CET."
Ce înseamnă concret:
- Intervalul 1 din CSV = 00:00–00:15 CET = 01:00–01:15 ora României
- Ultimul interval al zilei se termină la 00:00 CET = 01:00 ora României (ziua următoare)
- Diferența e mereu 1 oră (iarna CET→EET = +1h, vara CEST→EEST = +1h — se schimbă simultan)
Integrarea gestionează asta automat pe două niveluri:
- Calculele interne (intervalul curent, ziua de livrare, ferestrele optime) sunt făcute în CET — asigură sincronizarea exactă cu datele OPCOM
- Orele afișate în atributele senzorilor (ex:
ora: "22:45 → 23:45") sunt convertite automat în timezone-ul configurat în Home Assistant — nu trebuie să faci nicio conversie manuală
Nu trebuie să schimbi nimic în setările HA.
- Deschide HACS în Home Assistant
- Click pe cele 3 puncte (⋮) din colțul dreapta sus → Custom repositories
- Adaugă URL-ul:
https://github.com/cnecrea/opcom - Categorie: Integration
- Click Add → găsește „OPCOM România" → Install
- Restartează Home Assistant
- Copiază folderul
custom_components/opcom/în directorulconfig/custom_components/din Home Assistant - Restartează Home Assistant
- Settings → Devices & Services → Add Integration
- Caută „OPCOM" sau „OPCOM România"
- Completează formularul:
| Câmp | Descriere | Implicit | Interval permis |
|---|---|---|---|
| Limbă | Limba exportului CSV (ro sau en) |
ro |
ro, en |
| Rezoluții | Rezoluțiile în minute, separate prin virgulă | 15,30,60 |
Oricare din: 15, 30, 60 |
| Zile în avans | Câte zile să descarce (1 = doar azi, 2 = azi + mâine) | 2 |
1–3 |
| Interval actualizare | La câte minute să reîmprospăteze datele | 15 |
5–180 minute |
| Fereastră planificare | Durata ferestrei de preț optim (în minute) | 60 |
minim 15 minute |
| Număr ferestre | Câte ferestre optime să calculeze (top N) — ore acoperite = N × fereastră ÷ 60 | 6 |
1–24 |
Toate setările pot fi modificate după instalare, fără a șterge integrarea:
- Settings → Devices & Services → click pe integrarea OPCOM
- Click pe Configure (⚙️)
- Modifică setările dorite → Submit
- Integrarea se reîncarcă automat cu noile setări
Integrarea creează un device numit „OPCOM România". Sub el, pentru fiecare rezoluție configurată, se creează 10 entități (8 senzori + 2 senzori binari).
Cu configurarea implicită (3 rezoluții: 15, 30, 60) = 30 entități total.
| Entitate | Descriere | Unitate | Exemplu |
|---|---|---|---|
[15] Preț acum |
Prețul energiei în intervalul curent | RON/MWh | 322.56 |
[15] Preț următor |
Prețul energiei în intervalul următor | RON/MWh | 256.51 |
Atribute:
data: "2026-02-21"
rezolutie: "PT15M"
interval: 45
ora: "11:00 → 11:15"
zona: "Romania"| Entitate | Descriere | Unitate |
|---|---|---|
[15] Cea mai ieftină fereastră azi |
Prețul mediu al celei mai ieftine ferestre din ziua curentă | RON/MWh |
[15] Cea mai scumpă fereastră azi |
Prețul mediu al celei mai scumpe ferestre din ziua curentă | RON/MWh |
[15] Cea mai ieftină fereastră mâine |
La fel, pentru ziua următoare (Necunoscut până la publicare) | RON/MWh |
[15] Cea mai scumpă fereastră mâine |
La fel, pentru ziua următoare | RON/MWh |
Cum funcționează:
Senzorul afișează ca valoare principală (state) prețul mediu al celei mai bune ferestre. În atribute, sunt listate toate top N ferestrele, sortate după preț.
Ferestrele sunt non-suprapuse: algoritmul greedy alege cea mai bună fereastră, marchează acele intervale ca „ocupate", apoi caută următoarea cea mai bună din cele rămase. Asta garantează că fiecare fereastră acoperă ore diferite.
Ferestrele zilei includ și intervale deja trecute — sunt afișate ca referință istorică.
Atribute:
data: "2026-02-21"
rezolutie: "PT15M"
fereastra_minute: 60
numar_ferestre: 6
ferestre:
- ora: "00:00 → 01:00"
pret_mediu: "295.43 RON/MWh"
intervale: "1–4"
- ora: "03:00 → 04:00"
pret_mediu: "310.22 RON/MWh"
intervale: "13–16"
- ora: "02:00 → 03:00"
pret_mediu: "318.50 RON/MWh"
intervale: "9–12"| Entitate | Descriere | Unitate |
|---|---|---|
[15] Intervale rămase cumpărare azi |
Câte intervale ieftine mai rămân de acum încolo | număr |
[15] Intervale rămase vânzare azi |
Câte intervale scumpe mai rămân de acum încolo | număr |
Acești senzori arată câte intervale bune mai sunt în viitor. Valoarea scade natural spre 0 pe parcursul zilei, pe măsură ce intervalele trec.
Ferestrele sunt recalculate doar din intervale viitoare (min_interval = intervalul curent), deci nu iau în calcul ore deja trecute.
Atribute:
data: "2026-02-21"
rezolutie: "PT15M"
fereastra_minute: 60
numar_ferestre: 6
interval_curent: 45
intervale_ramase:
- interval: 73
ora: "18:00 → 18:15"
pret: "580.20 RON/MWh"
- interval: 74
ora: "18:15 → 18:30"
pret: "575.10 RON/MWh"| Entitate | Descriere | Pornit când... |
|---|---|---|
[15] Ar trebui să încarce acum |
Indică dacă e momentul optim pentru încărcare (preț mic) | Intervalul curent e într-o fereastră ieftină |
[15] Ar trebui să exporte acum |
Indică dacă e momentul optim pentru export/vânzare (preț mare) | Intervalul curent e într-o fereastră scumpă |
Comportament:
- Calculează ferestrele doar din intervale viitoare (nu recomandă intervale deja trecute)
- Folosește cross-day: combină datele de azi cu cele de mâine (dacă sunt disponibile). La ora 23:00, dacă mâine la 02:00 prețul e foarte mic, senzorul „ar trebui să încarce" va ține cont de asta
- Ferestrele sunt non-suprapuse (algoritm greedy)
Atribute:
data: "2026-02-21"
rezolutie: "PT15M"
fereastra_minute: 60
numar_ferestre: 6
interval_curent: 45
fereastra_activa:
ora: "11:00 → 12:00"
pret_mediu: "322.56 RON/MWh"
intervale: "45–48"Când nu e activ (Oprit), fereastra_activa este null.
Toate entitățile au un prefix care indică rezoluția: [15], [30], sau [60].
[15]= PT15M — intervale de 15 minute (96 pe zi). Cel mai granular. Disponibil doar pe OPCOM pentru anumite piețe.[30]= PT30M — intervale de 30 minute (48 pe zi).[60]= PT60M — intervale de 60 minute (24 pe zi). Cel mai comun.
Poți configura una, două, sau toate trei rezoluțiile. Dacă nu ai nevoie de granularitate, folosește doar 60.
- Sliding window: pentru fiecare poziție posibilă în ziua respectivă, se calculează media prețurilor pe durata ferestrei (ex: 4 intervale × 15 min = 60 min)
- Sortare: toate candidatele sunt sortate crescător (pentru ieftin) sau descrescător (pentru scump)
- Selecție greedy non-suprapusă: se alege cea mai bună fereastră, se marchează intervalele ca ocupate, apoi se alege următoarea cea mai bună care NU se suprapune cu cele deja selectate. Se repetă până se ating top N ferestre.
Cu rezoluție PT15M și fereastră de 60 minute (4 sloturi), pentru o zi cu 96 intervale:
- Se generează 93 de candidați (sliding window: 1–4, 2–5, ..., 93–96)
- Se sortează după preț mediu
- Se alege cea mai ieftină (ex: sloturi 1–4, medie 295 RON)
- Se marchează 1, 2, 3, 4 ca ocupate
- Orice candidat care include 1, 2, 3, sau 4 e eliminat
- Se alege următoarea (ex: sloturi 13–16, medie 310 RON)
- Se repetă până la top N (implicit 6)
Rezultat: 6 ferestre distincte temporal, fără suprapunere.
Senzorii binari și senzorii de intervale rămase primesc un parametru min_interval egal cu intervalul curent. Toate ferestrele care încep înainte de acest interval sunt eliminate din candidați. Astfel:
- La ora 14:00, nu ți se recomandă să „încarci" la 03:00 (deja trecut)
- Ferestrele se recalculează dinamic pe măsură ce trece ziua
Senzorii de ferestre (WindowsSensor) nu filtrează — arată toate ferestrele zilei ca referință.
La ora 23:00, senzorii binari combină:
- Intervalele rămase din azi (ex: 93–96 pentru PT15)
- Toate intervalele de mâine (ex: 97–192, cu offset)
Astfel pot recomanda încărcarea la 02:00 mâine chiar dacă azi nu mai sunt ferestre bune. Funcționează doar dacă datele de mâine sunt disponibile (publicate de OPCOM, de obicei după ora 13:00).
Cele două setări window_minutes și top_n_windows lucrează împreună:
Ore acoperite = top_n_windows × window_minutes ÷ 60
Aceeași formulă se aplică identic pe toate rezoluțiile (PT15, PT30, PT60). Integrarea nu diferențiază per rezoluție.
Exemplu: ai nevoie de 4 ore de cumpărare/vânzare → window_minutes = 60, top_n_windows = 4 → 4 × 60 ÷ 60 = 4 ore.
top_n_windows e prea mare, senzorii de cumpărare ȘI vânzare se activează simultan. Cu top_n = 16 și window_minutes = 60: 16 ore „ieftine" + 16 ore „scumpe" = 32, dar ziua are 24 → minim 8 ore se suprapun.
Regulă practică: top_n × window_minutes ÷ 60 nu ar trebui să depășească 8–10 ore. Detalii și tabel de referință în SETUP.md.
automation:
- alias: "Încarcă bateria la preț ieftin"
trigger:
- platform: state
entity_id: binary_sensor.ar_trebui_sa_incarce_acum_pt15_azi
to: "on"
action:
- service: switch.turn_on
target:
entity_id: switch.battery_charger
- alias: "Oprește încărcarea când se termină fereastra ieftină"
trigger:
- platform: state
entity_id: binary_sensor.ar_trebui_sa_incarce_acum_pt15_azi
to: "off"
action:
- service: switch.turn_off
target:
entity_id: switch.battery_chargerautomation:
- alias: "Exportă la preț scump"
trigger:
- platform: state
entity_id: binary_sensor.ar_trebui_sa_exporte_acum_pt15_azi
to: "on"
action:
- service: switch.turn_on
target:
entity_id: switch.grid_exportautomation:
- alias: "Prețuri mâine disponibile"
trigger:
- platform: state
entity_id: sensor.cea_mai_ieftina_fereastra_maine_pt60_maine
from: "unknown"
action:
- service: notify.mobile_app
data:
title: "OPCOM: Prețuri mâine"
message: >
Cea mai ieftină fereastră mâine:
{{ state_attr('sensor.cea_mai_ieftina_fereastra_maine_pt60_maine', 'ferestre')[0].ora }}
la {{ states('sensor.cea_mai_ieftina_fereastra_maine_pt60_maine') }} RON/MWhIntegrarea expune date complete de diagnosticare prin mecanismul standard HA:
- Settings → Devices & Services → click pe OPCOM
- Click pe cele 3 puncte (⋮) → Download diagnostics
Fișierul JSON conține:
- Configurația completă (data + options)
- Lista device-urilor și entităților create
- Toate datele raw descărcate de pe OPCOM (prețuri, volume, sumare)
- Starea ultimei actualizări (succes/eroare)
Util la troubleshooting sau la raportarea unui bug.
custom_components/opcom/
├── __init__.py # Setup/unload integrare + update_listener
├── api.py # Descărcare CSV + parsing OPCOM
├── binary_sensor.py # Senzori binari (ar trebui să încarce/exporte)
├── config_flow.py # Formularul de configurare + opțiuni
├── const.py # Constante, defaults, OpcomSettings dataclass
├── coordinator.py # DataUpdateCoordinator — fetch centralizat
├── diagnostics.py # Export diagnostics
├── helpers.py # Funcții comune (ferestre, formatare, cross-day)
├── manifest.json # Metadata integrare
├── sensor.py # Senzori (preț, ferestre, intervale rămase)
└── translations/
├── en/
│ └── strings.json # Traduceri EN (config flow)
└── ro/
└── ro.json # Traduceri RO (config flow)
- Home Assistant 2024.1.0 sau mai nou
- HACS (opțional, pentru instalare ușoară)
- Acces la internet — integrarea descarcă date de pe opcom.ro
Nu necesită dependențe externe (nu instalează pachete pip/npm).
-
Prețurile de mâine nu sunt disponibile imediat — OPCOM le publică de obicei între 13:00–15:00. Până atunci, senzorii „mâine" afișează „Necunoscut".
-
O singură instanță — integrarea suportă o singură configurare. Dacă încerci să adaugi a doua, vei primi eroare „already configured".
-
Rezoluția PT15M include date suplimentare — exportul CSV la 15 minute conține și coloane de volum tranzacționat + zonă. Rezoluțiile de 30 și 60 minute au doar preț. Asta vine de la OPCOM, nu de la integrare.
-
Cross-day funcționează doar dacă datele de mâine există — dacă
days_aheadeste setat la 1, nu se descarcă datele de mâine și funcția cross-day nu are efect.
Ciprian Nicolae (@cnecrea)