Skip to content

cnecrea/opcom

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

95 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OPCOM România — Integrare Home Assistant

HACS Custom GitHub Release Total descărcări pentru toate versiunile Descărcări pentru ultima versiune

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.).


Ce face integrarea

  • 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)
  • Două moduri de selecție binară: mod fereastră (intervale consecutive, medie) și mod individual (fiecare interval pe merit propriu)
  • Senzori binari pentru automatizări: ferestre optime + intervale individuale optime
  • Cross-day: senzorii binari văd și prețurile de mâine (când sunt disponibile) — util seara
  • Ferestre stabile: senzorii binari calculează ferestrele pe toată ziua — lista nu se schimbă pe parcursul zilei
  • 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

Sursa datelor

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.


Referință de timp: CET, nu ora României

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.


Instalare

HACS (recomandat)

  1. Deschide HACS în Home Assistant
  2. Click pe cele 3 puncte (⋮) din colțul dreapta sus → Custom repositories
  3. Adaugă URL-ul: https://github.com/cnecrea/opcom
  4. Categorie: Integration
  5. Click Add → găsește „OPCOM România" → Install
  6. Restartează Home Assistant

Manual

  1. Copiază folderul custom_components/opcom/ în directorul config/custom_components/ din Home Assistant
  2. Restartează Home Assistant

Configurare

Pasul 1 — Adaugă integrarea

  1. SettingsDevices & ServicesAdd Integration
  2. Caută „OPCOM" sau „OPCOM România"
  3. 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

Pasul 2 — Reconfigurare (opțional)

Toate setările pot fi modificate după instalare, fără a șterge integrarea:

  1. SettingsDevices & Services → click pe integrarea OPCOM
  2. Click pe Configure (⚙️)
  3. Modifică setările dorite → Submit
  4. Integrarea se reîncarcă automat cu noile setări

Entități create

Integrarea creează un device numit „OPCOM România". Sub el, pentru fiecare rezoluție configurată, se creează 12 entități (8 senzori + 4 senzori binari).

Cu configurarea implicită (3 rezoluții: 15, 30, 60) = 36 entități total.

Senzori de preț

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"

Senzori ferestre optime (azi + mâine)

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"

Senzori intervale rămase

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"

Senzori binari — mod fereastră (pentru automatizări)

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 pe toată ziua (azi + mâine cross-day) — lista de ferestre e stabilă și nu se schimbă pe parcursul zilei
  • Senzorul e ON doar când intervalul curent se află într-una din ferestrele selectate
  • 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.

Senzori binari — mod individual (pentru automatizări granulare)

Entitate Descriere Pornit când...
[15] Interval ieftin acum Intervalul curent e printre cele mai ieftine ale zilei Prețul propriu al intervalului curent e în top N
[15] Interval scump acum Intervalul curent e printre cele mai scumpe ale zilei Prețul propriu al intervalului curent e în top N

Diferența față de modul fereastră:

Modul fereastră selectează blocuri consecutive de intervale și calculează media. Asta înseamnă că un interval individual cu preț excelent poate fi exclus dacă vecinii lui au prețuri proaste (media e trasă în jos/sus).

Modul individual selectează fiecare interval pe baza prețului propriu, fără a ține cont de vecini. Acoperirea totală e identică (același număr de intervale), dar distribuția diferă.

Exemplu concret (PT15, window=60, top_n=4 → 16 intervale selectate):

Interval Preț Mod fereastră Mod individual
21:15 719 ✅ (media fereastrei 21–22 = 693) ✅ (#2)
21:30 723 ✅ (#1)
18:45 658 ❌ (media fereastrei 18–19 prea mică) ✅ (#5)
22:15 649 ❌ (media fereastrei 22–23 prea mică) ✅ (#6)

Când folosești fiecare mod:

  • Mod fereastră („Ar trebui să încarce/exporte"): când ai nevoie de perioade neîntrerupte — ciclu complet de încărcare baterie, programare EV
  • Mod individual („Interval ieftin/scump"): când vrei să maximizezi valoarea per interval — export grid la prețul cel mai mare posibil, reducere consum în vârfuri

Atribute:

data: "2026-02-21"
rezolutie: "PT15M"
mod: "individual"
intervale_selectate: 16
interval_curent: 45
interval_activ:
  interval: 45
  ora: "11:00 → 11:15"
  pret: "580.20 RON/MWh"
top_intervale:
  - interval: 86
    ora: "21:15 → 21:30"
    pret: "719.00 RON/MWh"
  - interval: 87
    ora: "21:30 → 21:45"
    pret: "723.00 RON/MWh"

Când nu e activ (Oprit), interval_activ este null.


Prefixul de rezoluție

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.


Algoritmul de ferestre

Cum funcționează

  1. 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)
  2. Sortare: toate candidatele sunt sortate crescător (pentru ieftin) sau descrescător (pentru scump)
  3. 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.

Algoritmul individual

Modul individual e mai simplu:

  1. Sortare directă: toate intervalele zilei sunt sortate după prețul propriu (crescător sau descrescător)
  2. Selecție top N: se aleg primele N intervale, unde N = top_n_windows × (window_minutes ÷ res_minutes)
  3. Verificare: senzorul e ON dacă intervalul curent e în lista selectată

Nu există concept de fereastră, medie, sau non-suprapunere — fiecare interval e evaluat independent.

Exemplu concret

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.

Stabilitatea ferestrelor (senzorii binari)

Senzorii binari (ambele moduri) calculează selecția pe toată ziua (fără filtrare pe intervalul curent). Asta garantează că lista e identică indiferent de ora la care o evaluezi — un interval selectat dimineață e selectat și seara.

Senzorul e ON doar când intervalul curent se află în selecție. Intervalele trecute ocupă locuri în top N, dar asta e corect: dacă cele mai ieftine ore chiar au fost dimineață, asta reflectă realitatea. Dacă ai nevoie de mai multă acoperire, mărește top_n_windows.

Senzorii de intervale rămase primesc un parametru min_interval egal cu intervalul curent — ei filtrează doar intervalele viitoare și recalculează dinamic.

Senzorii de ferestre (WindowsSensor) nu filtrează — arată toate ferestrele zilei ca referință.

Cross-day (senzorii binari)

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).

Cum calculezi setările corect

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.

⚠️ Atenție: dacă 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.


Exemple de automatizări

Încarcă bateria când prețul e mic (mod fereastră)

automation:
  - alias: "Încarcă bateria la preț ieftin"
    trigger:
      - platform: state
        entity_id: binary_sensor.15_ar_trebui_sa_incarce_acum
        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.15_ar_trebui_sa_incarce_acum
        to: "off"
    action:
      - service: switch.turn_off
        target:
          entity_id: switch.battery_charger

Exportă energie când prețul e mare (mod individual)

automation:
  - alias: "Exportă la preț scump (per interval)"
    trigger:
      - platform: state
        entity_id: binary_sensor.15_interval_scump_acum
        to: "on"
    action:
      - service: switch.turn_on
        target:
          entity_id: switch.grid_export

  - alias: "Oprește exportul"
    trigger:
      - platform: state
        entity_id: binary_sensor.15_interval_scump_acum
        to: "off"
    action:
      - service: switch.turn_off
        target:
          entity_id: switch.grid_export

Notificare când se publică prețurile de mâine

automation:
  - alias: "Prețuri mâine disponibile"
    trigger:
      - platform: state
        entity_id: sensor.60_cea_mai_ieftina_fereastra_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.60_cea_mai_ieftina_fereastra_maine', 'ferestre')[0].ora }}
            la {{ states('sensor.60_cea_mai_ieftina_fereastra_maine') }} RON/MWh

Diagnostics

Integrarea expune date complete de diagnosticare prin mecanismul standard HA:

  1. SettingsDevices & Services → click pe OPCOM
  2. 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.


Structura fișierelor

custom_components/opcom/
├── __init__.py          # Setup/unload integrare + update_listener
├── api.py               # Descărcare CSV + parsing OPCOM
├── binary_sensor.py     # Senzori binari (fereastră + individual)
├── 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, individual, 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)

Cerințe

  • 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).


Limitări cunoscute

  1. 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".

  2. O singură instanță — integrarea suportă o singură configurare. Dacă încerci să adaugi a doua, vei primi eroare „already configured".

  3. 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.

  4. Cross-day funcționează doar dacă datele de mâine există — dacă days_ahead este setat la 1, nu se descarcă datele de mâine și funcția cross-day nu are efect.


☕ Susține dezvoltatorul

Dacă ți-a plăcut această integrare și vrei să sprijini munca depusă, invită-mă la o cafea! 🫶
Nu costă nimic, iar contribuția ta ajută la dezvoltarea viitoare a proiectului. 🙌

Buy Me A Coffee

Mulțumesc pentru sprijin și apreciez fiecare gest de susținere! 🤗


🧑‍💻 Contribuții

Contribuțiile sunt binevenite! Simte-te liber să trimiți un pull request sau să raportezi probleme aici.


🌟 Suport

Dacă îți place această integrare, oferă-i un ⭐ pe GitHub! 😊

About

OPCOM PZU România → senzori Home Assistant (PT15M/PT30M/PT60M), preț curent/următor, ferestre ieftin/scump (greedy), cross-day, diagnostics.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages