Skip to content

Conversation

@luuuis
Copy link

@luuuis luuuis commented Aug 10, 2025

Proposed change

Add an integration for retrieving Iberian Peninsula energy price data from OMIE. The integration retrieves data from the daily market run and then updates the sensors accordingly on an hourly basis.

This is (simplified) port of https://github.com/luuuis/hass_omie/, also developed by myself, which has proven itself stable over the last years.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Follow-up of #108045 (stale)

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request: Add OMIE integration home-assistant.io#40448
  • Link to developer documentation pull request:
  • Link to frontend pull request: n/a

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • [] For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

luuuis added 30 commits January 14, 2024 18:39
* Port code over from luuuis/hass_omie and adapt to use pyomie client
  library
@luuuis luuuis marked this pull request as ready for review September 12, 2025 17:53
@home-assistant home-assistant bot requested a review from abmantis September 12, 2025 17:53
@dgomes
Copy link
Contributor

dgomes commented Sep 16, 2025

@luuuis I think this should still go ahead, but have a look at #145848 (comment)

@abmantis abmantis marked this pull request as draft September 23, 2025 13:51
@abmantis
Copy link
Member

Marking as draft until #150399 (comment) is sorted out.
By the way @luuuis ruff is failing on the CI.

@abmantis
Copy link
Member

By the way, I just tried running this and got an error:

2025-10-16 17:24:15.427 ERROR (MainThread) [homeassistant.components.omie.coordinator] Unexpected error fetching omie data
Traceback (most recent call last):
  File "/home/acosta/dev/repos/homeassistant/core/review/homeassistant/helpers/update_coordinator.py", line 392, in _async_refresh
    self.data = await self._async_update_data()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/acosta/dev/repos/homeassistant/core/review/homeassistant/components/omie/coordinator.py", line 57, in _async_update_data
    if results := await pyomie.spot_price(self._client_session, date):
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/acosta/dev/repos/homeassistant/core/review/.venv/lib/python3.13/site-packages/pyomie/main.py", line 122, in spot_price
    return await _fetch_and_make_results(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        client_session, source, dc.date, _make_spot_data
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/acosta/dev/repos/homeassistant/core/review/.venv/lib/python3.13/site-packages/pyomie/main.py", line 90, in _fetch_and_make_results
    row[0]: [_to_float(row[h]) for h in _HOURS if len(row) > h and row[h]]
             ~~~~~~~~~^^^^^^^^
  File "/home/acosta/dev/repos/homeassistant/core/review/.venv/lib/python3.13/site-packages/pyomie/main.py", line 151, in _to_float
    return float(n.replace(",", "."))
ValueError: could not convert string to float: 'H1Q1'

@luuuis luuuis marked this pull request as ready for review November 3, 2025 12:45
@luuuis luuuis requested a review from dgomes November 3, 2025 12:45
@luuuis
Copy link
Author

luuuis commented Nov 3, 2025

Last changes:

  1. Updated to use pyomie 1.0.1 that now also uses quarter-hourly pricing (introduced in 2025-10-01)
  2. Adapted coordinator to schedule sensor updates every 15 m.
  3. Removed get_market_dates logic related to fetching multiple CET days for historical/future time periods. Only the minimum required is done for the current time period (in practice, at ~00:00 CET a new day of data will be fetched).


return {
dt.datetime.fromisoformat(dt_str).astimezone(CET): v
for dt_str, v in localize_quarter_hourly_data(market_date, series_data).items()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v is the price? if so, lets rename it to price

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v contains the series values depending on the series. There are series for prices, import/export power and so forth, although in practice we are only reading the price-related series into the sensors.

series_value would be an accurate and descriptive variable name, if somewhat verbose.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

series_values (plural) then?
anything that tells what the variable is fine, because v is too opaque.

from __future__ import annotations

from collections.abc import Mapping
import datetime as dt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if possible, lets use now from homeassistant.util.dt instead of datetime.datetime

@home-assistant home-assistant bot marked this pull request as draft November 3, 2025 18:53
@abmantis
Copy link
Member

abmantis commented Nov 3, 2025

It seems that this is fetching data 1 hour ahead. Currently at 19:00 UTC / 20:00 CET, it shows 0.1572 €/kWh which is the price for H21Q1 on the website.

for date in {d for d in market_dates if d not in data}:
_LOGGER.debug("Fetching OMIE data for %s", date)
if results := await pyomie.spot_price(self._client_session, date):
_LOGGER.debug("pyomie.spot_price returned: %s", results)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this prints a very big and ugly unformulated message 😅
can we remove it and maybe have logs in the library instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh it's big for sure, especially since the transition to 15-minute pricing. I'm not sure what you mean by ugly here and it seems like that would be highly subjective anyway–it's a JSON payload returned by an external lib. 🤷

The rationale to have it is that DEBUG logs on this component would be used for troubleshooting only and it's normally only logged every 24 hours anyway. It feels like by removing it we would hamper our ability to troubleshoot potential future issues for I'm not sure what benefit?

Since pyomie's only function is to print the fetched data formatted as JSON to stdout, it would be silly to also duplicate that into logs. Having said that, I could imagine a separate logger in HA for this that doesn't propagate messages to the parent logger and therefore has to be set explicitly. Something like:

_LOGGER = logging.getLogger(__name__)
# Separate logger for pyomie payloads that doesn't inherit
_PYOMIE_LOGGER = logging.getLogger(f'{__name__}.pyomie')
_PYOMIE_LOGGER.propagate = False
_PYOMIE_LOGGER.setLevel(logging.WARNING)

I haven't seen this pattern used in HA elsewhere, however. WDYT?

Copy link
Member

@abmantis abmantis Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I said ugly because it is not just json, it is printing data structs/classes:

2025-11-04 19:40:59.554 DEBUG (MainThread) [homeassistant.components.omie.coordinator] pyomie.spot_price returned: OMIEResults(updated_at=datetime.datetime(2025, 11, 4, 19, 40, 59, 554411, tzinfo=datetime.timezone.utc), market_date=datetime.date(2025, 11, 4), contents=SpotData(url='https://www.omie.es/sites/default/files/dados/AGNO_2025/MES_11/TXT/INT_PBC_EV_H_1_04_11_2025_04_11_2025.TXT', market_date='2025-11-04', header='OMIE - Mercado de electricidad;Fecha Emisión :03/11/2025 - 14:14;;04/11/2025;Precio del mercado diario (EUR/MWh);;;;', es_pt_total_power=[29418.9, 29014.8, 28514.6, 28036.4, 27595.0, 27341.3, 27035.9, 27004.3, 26461.1, 26251.3, 26126.3, 26082.7, 26046.5, 26007.4, 26039.5, 26068.4, 26007.5, 26072.9, 26137.7, 26182.7, 26385.2, 26551.6, 26687.7, 26923.3, 28743.2, 28930.3, 29519.9, 30033.6, 31538.1, 32463.2, 33191.8

It also has things like this, with \r\n for example:

raw='OMIE - Mercado de electricidad;Fecha Emisión :03/11/2025 - 14:14;;04/11/2025;Precio del mercado diario (EUR/MWh);;;;\r\n\r\n;H1Q1;H1Q2;H1Q3;H1Q4;H2Q1;H2Q2;H2Q3;H2Q4;H3Q1;H3Q2;H3Q3;H3Q4;H4Q1;H4Q2;H4Q3;H4Q4;H5Q1;H5Q2;H5Q3;H5Q4;H6Q1;H6Q2;H6Q3;H6Q4;H7Q1;H7Q2;H7Q3;H7Q4;H8Q1;H8Q2;H8Q3;H8Q4;H9Q1;H9Q2;H9Q3;H9Q4;H10Q1;H10Q2;H10Q3;H10Q4;H11Q1;H11Q2;H11Q3;H11Q4;H12Q1;H12Q2;H12Q3;H12Q4;H13Q1;H13Q2;H13Q3;H13Q4;H14Q1;H14Q2;H14Q3;H14Q4;H15Q1;H15Q2;H15Q3;H15Q4;H16Q1;H16Q2;H16Q3;H16Q4;H17Q1;H17Q2;H17Q3;H17Q4;H18Q1;H18Q2;H18Q3;H18Q4;H19Q1;H19Q2;H19Q3;H19Q4;H20Q1;H20Q2;H20Q3;H20Q4;H21Q1;H21Q2;H21Q3;H21Q4;H22Q1;H22Q2;H22Q3;H22Q4;H23Q1;H23Q2;H23Q3;H23Q4;H24Q1;H24Q2;H24Q3;H24Q4;\r\nPrecio marginal en el sistema español (EUR/MWh);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since pyomie's only function is to print the fetched data formatted as JSON to stdout, it would be silly to also duplicate that into logs.

You can make it a debug log, and set the library log level to info or warning by default. Would that work for you?

@luuuis
Copy link
Author

luuuis commented Nov 4, 2025

It seems that this is fetching data 1 hour ahead. Currently at 19:00 UTC / 20:00 CET, it shows 0.1572 €/kWh which is the price for H21Q1 on the website.

0.1572 is the correct price for 2025-11-03 at 19:00 in Portugal. OMIE hours are 1-based, you can read all about it in Interpretação OMIE mercado diário (not updated for quarter-hourly prices but the same logic applies).

Full results for 2025-11-03
~ ❯❯❯ pipx run pyomie 2025-11-03 | jq '.pt_spot_price | to_entries | map({
  key: (
    (.key * 15 / 60 | floor | tostring) as $hour |
    (.key * 15 % 60 | tostring) as $min |
    (if ($hour | length) == 1 then "0" + $hour else $hour end) + ":" +
    (if ($min | length) == 1 then "0" + $min else $min end)
  ),
  value: (.value*10|round/10000)
}) | from_entries'
{
  "00:00": 0.0921,
  "00:15": 0.0901,
  "00:30": 0.0875,
  "00:45": 0.0861,
  "01:00": 0.0846,
  "01:15": 0.0837,
  "01:30": 0.0836,
  "01:45": 0.0834,
  "02:00": 0.0832,
  "02:15": 0.083,
  "02:30": 0.0829,
  "02:45": 0.0829,
  "03:00": 0.0827,
  "03:15": 0.0824,
  "03:30": 0.0824,
  "03:45": 0.0827,
  "04:00": 0.0824,
  "04:15": 0.0827,
  "04:30": 0.083,
  "04:45": 0.0832,
  "05:00": 0.0836,
  "05:15": 0.0837,
  "05:30": 0.0854,
  "05:45": 0.0913,
  "06:00": 0.095,
  "06:15": 0.1028,
  "06:30": 0.105,
  "06:45": 0.1101,
  "07:00": 0.1187,
  "07:15": 0.1239,
  "07:30": 0.1404,
  "07:45": 0.1494,
  "08:00": 0.1432,
  "08:15": 0.1187,
  "08:30": 0.1056,
  "08:45": 0.0875,
  "09:00": 0.0905,
  "09:15": 0.0827,
  "09:30": 0.065,
  "09:45": 0.0454,
  "10:00": 0.05,
  "10:15": 0.0378,
  "10:30": 0.0301,
  "10:45": 0.0187,
  "11:00": 0.0285,
  "11:15": 0.0191,
  "11:30": 0.0167,
  "11:45": 0.0155,
  "12:00": 0.0168,
  "12:15": 0.0167,
  "12:30": 0.0168,
  "12:45": 0.0157,
  "13:00": 0.0152,
  "13:15": 0.0129,
  "13:30": 0.0129,
  "13:45": 0.014,
  "14:00": 0.0143,
  "14:15": 0.0167,
  "14:30": 0.0197,
  "14:45": 0.0238,
  "15:00": 0.019,
  "15:15": 0.0359,
  "15:30": 0.0397,
  "15:45": 0.0506,
  "16:00": 0.045,
  "16:15": 0.0511,
  "16:30": 0.0725,
  "16:45": 0.0834,
  "17:00": 0.0854,
  "17:15": 0.1039,
  "17:30": 0.1118,
  "17:45": 0.1404,
  "18:00": 0.1187,
  "18:15": 0.1233,
  "18:30": 0.129,
  "18:45": 0.1292,
  "19:00": 0.1404,
  "19:15": 0.15,
  "19:30": 0.15,
  "19:45": 0.15,
  "20:00": 0.1572,
  "20:15": 0.1553,
  "20:30": 0.15,
  "20:45": 0.1432,
  "21:00": 0.15,
  "21:15": 0.135,
  "21:30": 0.1217,
  "21:45": 0.1153,
  "22:00": 0.1105,
  "22:15": 0.105,
  "22:30": 0.1002,
  "22:45": 0.0934,
  "23:00": 0.0967,
  "23:15": 0.0945,
  "23:30": 0.09,
  "23:45": 0.0861
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants