Skip to content

Instantly share code, notes, and snippets.

@luuuis
Last active February 18, 2023 18:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luuuis/25a1030327a6ff9713a5a6747ebb6154 to your computer and use it in GitHub Desktop.
Save luuuis/25a1030327a6ff9713a5a6747ebb6154 to your computer and use it in GitHub Desktop.
Preços do mercado diário e do mecanismo de ajuste MIBEL
homeassistant:
customize:
sensor.omie_daily_spot_pt:
device_class: monetary
state_class: measurement
sensor.omie_daily_ajuste_pt:
device_class: monetary
state_class: measurement
sensor:
- platform: command_line
unique_id: omie_daily_spot_pt
name: omie_daily_spot_pt
command: "python3 /config/omie.py spot"
value_template: "{{ value_json.spot_price_pt_hourly[now().hour] }}"
unit_of_measurement: 'EUR/MWh'
json_attributes:
- row
- header
- source
- spot_price_pt_day_average
- spot_price_pt_hourly
- platform: command_line
name: omie_daily_ajuste_pt
unique_id: omie_daily_ajuste_pt
command: "python3 /config/omie.py ajuste"
value_template: "{{ value_json.ajuste_price_pt_hourly[now().hour] }}"
unit_of_measurement: 'EUR/MWh'
json_attributes:
- row
- header
- source
- ajuste_price_pt_day_average
- ajuste_price_pt_hourly
- platform: command_line
unique_id: omie_tomorrow_spot_pt
name: omie_tomorrow_spot_pt
command: "python3 /config/omie.py spot {{ (today_at()+timedelta(days=1)).date().isoformat() }}"
scan_interval: 3600
value_template: >
{% if value != '' %}
{{ value_json.spot_price_pt_hourly[now().hour] }}
{% else %}
{{ 'unavailable' }}
{% endif %}
unit_of_measurement: 'EUR/MWh'
json_attributes:
- row
- header
- source
- spot_price_pt_day_average
- spot_price_pt_hourly
- platform: command_line
name: omie_tomorrow_ajuste_pt
unique_id: omie_tomorrow_ajuste_pt
command: "python3 /config/omie.py ajuste {{ (today_at()+timedelta(days=1)).date().isoformat() }}"
scan_interval: 3600
value_template: "{{ value_json.ajuste_price_pt_hourly[now().hour] }}"
unit_of_measurement: 'EUR/MWh'
json_attributes:
- row
- header
- source
- ajuste_price_pt_day_average
- ajuste_price_pt_hourly
#!/usr/bin/env python3
import csv
import json
import statistics
import sys
from datetime import date, datetime
import requests
def to_dict(source, fetch_date, short_names):
with requests.get(source) as resp:
if resp.status_code == 404:
return None
lines = resp.text.splitlines()
header = lines[0]
data = lines[2:]
reader = csv.reader(data, delimiter=';', skipinitialspace=True)
rows = {row[0]: [float(row[i + 1].replace(',', '.')) for i in list(range(24))] for row in reader if row[0] != ''}
file_data = {
'header': header,
'fetched': datetime.now().isoformat(),
'market_date': fetch_date.isoformat(),
'source': source,
}
for k in rows:
hourly = rows[k]
if k in short_names:
# hourly & daily avg/sum
key = short_names[k]
suffix, daily = ['average', round(statistics.mean(hourly), 2)] if "(EUR/MWh)" in k else ['total', round(sum(hourly), 1)]
file_data.update({
f'{key}_day_{suffix}': daily,
f'{key}_hourly': hourly,
})
else:
# unprocessed:
file_data.update({k: hourly})
return file_data
def spot_url(yy, MM, dd):
dd_MM_yy = f'{dd}_{MM}_{yy}'
return f'https://www.omie.es/sites/default/files/dados/AGNO_{yy}/MES_{MM}/TXT/INT_PBC_EV_H_1_{dd_MM_yy}_{dd_MM_yy}.TXT'
def ajuste_url(yy, MM, dd):
dd_MM_yy = f'{dd}_{MM}_{yy}'
return f'https://www.omie.es/sites/default/files/dados/AGNO_{yy}/MES_{MM}/TXT/INT_MAJ_EV_H_{dd_MM_yy}_{dd_MM_yy}.TXT'
# _daily_total
# _daily_average
def spot_price(fetch_date):
source = spot_url(fetch_date.year, str.zfill(str(fetch_date.month), 2), str.zfill(str(fetch_date.day), 2))
return to_dict(source, fetch_date, {
"Energía total con bilaterales del mercado Ibérico (MWh)": 'energy_with_bilaterals_es_pt',
"Energía total de compra sistema español (MWh)": 'energy_purchases_es',
"Energía total de compra sistema portugués (MWh)": 'energy_purchases_pt',
"Energía total de venta sistema español (MWh)": 'energy_sales_es',
"Energía total de venta sistema portugués (MWh)": 'energy_sales_pt',
"Energía total del mercado Ibérico (MWh)": 'energy_es_pt',
"Exportación de España a Portugal (MWh)": 'energy_export_es_to_pt',
"Importación de España desde Portugal (MWh)": 'energy_import_es_from_pt',
"Precio marginal en el sistema español (EUR/MWh)": 'spot_price_es',
"Precio marginal en el sistema portugués (EUR/MWh)": 'spot_price_pt',
})
def ajuste_price(fetch_date):
source = ajuste_url(fetch_date.year, str.zfill(str(fetch_date.month), 2), str.zfill(str(fetch_date.day), 2))
return to_dict(source, fetch_date, {
"Precio de ajuste en el sistema español (EUR/MWh)": 'ajuste_price_es',
"Precio de ajuste en el sistema portugués (EUR/MWh)": 'ajuste_price_pt',
"Energía horaria sujeta al MAJ a los consumidores MIBEL (MWh)": 'ajuste_energy',
"Energía horaria sujeta al mecanismo de ajuste a los consumidores MIBEL (MWh)": 'ajuste_energy',
"Cuantía unitaria del ajuste (EUR/MWh)": 'ajuste_quantity',
})
if __name__ == '__main__':
if len(sys.argv) not in [2, 3] or not sys.argv[1] in ['spot', 'ajuste']:
print('usage: omie.py <spot|ajuste> [ISO date]', file=sys.stderr)
sys.exit(1)
fetch_date = date.today() if len(sys.argv) == 2 else date.fromisoformat(sys.argv[2])
if sys.argv[1] == 'spot':
spot = spot_price(fetch_date)
if spot is not None:
print(json.dumps(spot))
if sys.argv[1] == 'ajuste':
ajuste = ajuste_price(fetch_date)
if ajuste is not None:
print(json.dumps(ajuste))
@jonferreira
Copy link

Boas!
Visto que os valores são atualizados uma vez por dia, há necessidade de o HA estar a atualizar o sensor a cada 60s?
Não sou muito de python mas pelo que entendo o que fazes é ler o TXT mas processar só a linha do dia e hora atual?

@luuuis
Copy link
Author

luuuis commented Feb 10, 2023

@jonferreira não é necessário, no entanto também não descobri maneira de (com o Command Line Sensor) actualizar só às 00:00. Por outro lado parece-me inofensivo que actualize com mais ou menos frequência, os valores não mudam ao longo do dia e o ficheiro parece-me estar "cached".

pelo que entendo o que fazes é ler o TXT mas processar só a linha do dia e hora atual?

Se reparares no value_template do sensor este está configurado para assumir o valor da média do dia. No entanto, os dados restantes do dia estão lá nos atributos. Exemplo para um sensor do preço horário:

template:
  sensor:
    - unique_id: omie_daily_spot_hourly_pt
      device_class: monetary
      unit_of_measurement: 'EUR'
      state_class: measurement
      state: "{{ state_attr('sensor.omie_daily_spot_avg_pt', 'spot_prices_pt').hourly[now().hour]|float / 1000 }}"
      attributes:
        friendly_name: "OMIE preço horário do mercado diário (€/kWh)"

https://gist.github.com/luuuis/25a1030327a6ff9713a5a6747ebb6154/88816e2a91b02c6015b97c5e9bbdcadb286562ce#file-configuration-yml-L25-L33

@jonferreira
Copy link

@luuuis pelo que entendo podes usar o scan_interval - https://www.home-assistant.io/integrations/sensor.command_line/#scan_interval

podes meter 3600 para ser de hora a hora por exemplo. desta forma reduzes drasticamente o trafego desnecessário a ir buscar dados estáticos todos os minutos.

deixo tambem a pergunta: faria sentido atualizar o sensor com os valores horários "reais" e depois ter um outro sensor com a média? - https://www.home-assistant.io/integrations/min_max/

desta forma ficamos com um grafico horario e um diario, por assim dizer?

@luuuis
Copy link
Author

luuuis commented Feb 10, 2023

Pode-se fazer isso @jonferreira, o problema é que não há forma de garantir que ele actualiza atempadamente à meia noite. Como podes ver aqui em casa actualizou às 00:00:59, com esse scan_interval abres a possibilidade de ele actualizar à 1 da manhã.

Home – Home Assistant 2023-02-10 16-30-16

Pode ser que no futuro eu dedique uns tempos a fazer disto uma integração que só actualiza uma vez por dia mas para já não me importo de gastar uns bytes uma vez por minuto e estes ficheiros estão com certeza "cached" no OMIE.

faria sentido atualizar o sensor com os valores horários "reais" e depois ter um outro sensor com a média?

Pode fazer sentido consoante a necessidade de cada um. Se reparares já actualizei os exemplos hoje de manhã e estão lá os sensores da média diária omie_daily_spot_avg_pt e o horário omie_daily_spot_hourly_pt. Com o https://github.com/RomRider/apexcharts-card por exemplo já consegue fazer gráficos com o valor horário até ao fim do dia usando somente os atributos do omie_daily_spot_avg_pt porque tem os valores futuros também.

Home Assistant - Home – Home Assistant 2023-02-10 16-35-31

Já para o HA gravar os dados no histórico e estatísticas necessitamos do sensor omie_daily_spot_hourly_pt com o valor horário . 👍

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