Pörssisähkön mittauksen muutos

Tervehdys!

Varmistin tänään omalta sähköyhtiöltä (PKS), että asiakashinnoittelussa tullaan siirtymään kevään 2025 aikana pörssisähkön kohdalla 15 min välein päivittyviin hintoihin. Todennäköisesti Suomi menee tässäkin etunenässä, kun muu Eurooppa odottaa ja katsoo miten meille käy.

Tämähän luo kinkkisen ongelman Homeyn / Power by the Hour pörssisähkö ohjaukseen. Ohjelma ei tule enää toimimaan halutulla tavalla.

Onko kukaan ehtinyt vielä pohtia miten/ millä ohjauksen saisi toimimaan muutoksen jälkeen tai laskemaan esimerkiksi tunnille keskiarvo hinnan? Lämmityksen ohjaus 15 min intervalleissa on järjetöntä.

1 Like

Pohtinut ihan samaa asiaa. Pitäisikin kysellä PbtH tekijältä, että oliko asiaa jo ehtinyt pohtia. Olen myös PKS-asiakas, kulutan ja myyn.

1 Like

Kysyin tuota jo aiemmin, mutta näyttää siltä ettei hinnoittelun muutosta ole uutisoitu muualla Euroopassa kovin paljon.

Vaatisi sovelluksen uudelleen suunnittelua.

Täytyy varmaan ootella tuleeko tuo johonkin äppiin saataville.

En vaan oo ihan varma miten sähkön vähittäismyyjät tähän lähtevät mukaan. Sähkön myyjistä riippuu miten koska tuo tulee normi kuluttajille saataville. Jos tarjoavat edelleen tuntipohjaista hinnoittelua, niin sitten homma jatkuu sillä niinkuin nytkin.

1 Like

Tätä juuri kysyin ja ainakin PKS siirtyy siis kuluttaja-asiakkaiden kohdalla keväällä 25 varttihinnoitteluun. Nähtäväksi jää siirtyvätkö kaikki yhtiöt.

1 Like

Minä mietiskelin, että jos ei sovellukset taivu varttisähköön niin laittaisin shellyn blugit lukemaan pörssäriä ja ottamaan tiedon sieltä. Näiden pohjalta sitten flowt kohdilleen.

Ja nyt tuli mieleen, että miksi ne ei taipuisi? Sovelluksissahan on “uusi hinta saapui” kortit. Eikö nämä muutu sit vaan 15 välein?

Arvaan, että ne eivät hae kutenkiaan vartin välein hintatietoja, sillä seuraavan päivän hinnat julkaistaa klo 14-15 aikaan. Kortti siis herää kun on tullut seuraavan päivän hinnat.

Tuo uusi “hinta saapui” trigger käynnistyy kyllä nykyisin joka tunti, joten periaatteessa voisi käynnistyä myös vartin välein.

Itse kyllästyin PbH hintojen päivityksen hitauteen (oli useita tunteja myöhässä usein), ja kaikki lämmityspäätökset perustuukin Sähkötin – API ja Pörssisähkön hintaennuste hakujen yhdistämiseen ja samantien 15min jaksoihin jakamiseen (eli tulevaa odotellessa vielä sama hinta 4 x 15min).


(PbH hinnat luetaan kanssa, jos tulee ekana, mutta vaikkapa viimeiset 31pv ollut aina sähkötin:tä jäljessä)
(ennuste päivittyy 4 x vrk, mutta uusia tunteja tulee nykyään vain 03:xx laskennan jälkeen)

Uskon, että nuo lähteet muuttuu tarpeen mukaan 15min syklille, kun (onneksi) molemmat keskittyvät vain Suomen hintoihin :slight_smile:

PbH edelleen käytössä, mutta vain simppeleissä “onko tämä tunti halpa” vaikkapa auton ylläpidossa (oma script tarkoitus tehdä jotta myös ennuste tarvittaessa mukana)

1 Like

Useimmat käyttävät ENTSOE:n apia näihin ja siellä on jo tuki 15min resoluutiolle. Mm Itävallan hinnoissa tulee jo vartti ja tunti hinnat.

Mutta isoin ongelma monella kehittäjälle edessä on juuri sisäiset sovelluksen rakenteet, jotka menee uusiksi jos ei ole alunalkaenkaan pohtinut, että tähän voisi tulla muutos.

1 Like

Oletko saanut haettua noista API-rajapinnoista siis suoraan Homeyhyn jotenkin datat?

Joo, vaatii hieman raaka-dataan paneutusmista, ja varmasti voit tehdä paremman / yksikertaisemman kun mulle tuli ajan kanssa (olin täysin newbee Javan (HomeyScript kanssa) - muutama poiminta alla jos vaikka apua sulle:

Näillä voit hakea raaka-datan (poistin virhekäsittelyt yms. selvyyden vuoksi):

  const timeTarget = new Date(Math.floor(new Date().getTime()/(60*60*1000)) * (60*60*1000));
  var targetWebFixed = 'https://sahkotin.fi/prices?start=' + timeTarget;
  const res = await fetch(targetWebFixed);
  var elFixed = await res.json();
 
  var targetWebForecast = 'https://raw.githubusercontent.com/vividfog/nordpool-predict-fi/main/deploy/prediction.json'
  const res2 = await fetch(targetWebForecast);
  var elForecast = await res2.json();

ja tee tulos-JSON:lla sitten mitä haluat - vaikkapa fixed data (jossa ei AVL:tä) käsittelen itse näin että cent/kWh:

     var elFixedTimestamps = elFixed.prices.map(entry => {
       const shortenedDate = entry.date.slice(0, -8) + "Z";
       elCombined[shortenedDate] = (entry.value / 10).toFixed(4);
       return Date.parse(shortenedDate);
      });

PbH lisäys elFixed:iin, jos sahkotin myöhässä (vien koodille PbH JSON:n argumenttina…):

  for (let key in elPricePbH) {
    let newTimeStamp = Math.floor(new Date().getTime() / (60*60*1000) + Number(key) ) * (60*60*1000);
    if (newTimeStamp > lastTimestamp) {
      let isoNewTimestamp = (new Date(newTimeStamp).toISOString()).slice(0, -8) + "Z";
      elCombined[isoNewTimestamp] = (elPricePbH[key]*100).toFixed(5);
      elFixedTimestamps.push(isoNewTimestamp);
    }
  }

Jonka jäkeen (korjaan hieman vatkaimen ennustetta ja) lisään elFixed datan jatkoksi…

Erillisten scriptien trikkaus sitten sopivasti (kokemuksen perusteellla hyvä olla henkselit ja vyö periaate). Ja tähän jatkoksi lasketaan site-hinnat (mulla mokki-lämmitys saman Homey:n takana etänä, eri soppari) sitten 15min tasolla koko-hinta (eli siirtokulut ja ALV mukana)

“miksi tehdä asiat yksinkertaisesti kun ne voi tehdä monimutkaisesti” :slight_smile:

2 Likes

Otin yhteyttä kehittäjään, ja nyt saa ladattua myös 15min jaksolla ( “&quarter” vaan normikyselyn perään perään), vaikkapa siis:
sahkotin.fi/prices?fix&vat&start=2025-01-04T20:00:00.000Z&quarter

:star_struck: ja eiku muuttamaan vaan muutamaa kohtaa koodissa…

Jos muuten tulee tarvetta 10vrk ennusteelle (koska nyt “tiedät” 7pv eteenpäin hinnat), tällä saat

var targetWeb = 'https://api.spot-hinta.fi/PostalCodeTemperatures/' + postalCode + 'HomeAssistant=true';
const res = await fetch(targetWeb);
  var body = await res.json();

(lasken puuttuvat tunnit interpoloimalla ja keskiarvot niihin tästä hetkestä, niin ei tarvii arvailla miten vaikkapa 47.5h päähän kannattaa lämitystä ajaa :slight_smile:

Jos vaikka jollain olis uusio-käyttöä… Jätin alle mukaan input-esimerkin (tai helppo muutaa PbH-JSON:llekin) (koodi ei varmasti optimia, mutta tuntuu ajavan asiansa :grimacing: )

Nyt vihdoin jaksoin tehdä sen, mitä aikanaan PbH:lta kaipailin (eli etupeltoon katselua ja samojen hintojen priorisointia…)
Alla Then-kortilla kysytään koko 15min hinnoista, onko 110h :rofl: päässä oleva 15min jakso (minulla ei tarvetta laskea pidempiä yhtenäisiä jaksoja, vielä :yum:) siitä seuraavalla 4h 15min jaksolla kaikista halvin 15min (paluu true/false), priorisoiden tässä ekana tulevan jos/kun samoja hintoja.
Samalla luodaan dedicate-tag:ejä (koodi ajettava kerran halutulla tagStart:lla ennen flow-tagejä), joista voi käyttää/päätellä halutessaan muutakin…
(Samalla tuli ratkaistua, miten saan skaalautuvasti/systemaattisesti/suht.helposti scriptiin ja scriptistä takaisin tuloksia ilman turhien variant ja/tai virtual device manuaalusta perustamista)


//El Price evaluation 

const allArguments = args[0];
//const sepArguments = allArguments.split(';').map(value => 
//   (value.startsWith('{') || value.startsWith(' {')) ? value : value.trim());
const sepArguments = allArguments.split(';').map(value => 
    value.includes('{') ? value : value.trim()
);

//--------------------------------------
  const tagStart = sepArguments[0].toString() + '_';
//--------------------------------------
  const elJSON = JSON.parse(sepArguments[1]);
  const elIncrement = parseFloat(sepArguments[2]); // zero => flat, positive  => earlier value, neg. => later 
  const startHours = parseFloat(sepArguments[3]); // ++ hours fron now (also as ref. value)
  const totalHours = parseFloat(sepArguments[4]); // observed hours
  const amongLowest = parseFloat(sepArguments[5]); // to be one of lowest observed 15min periods 
//--------------------------------------

//excample
  // const tagStart = 'elFuture_test_';
  // const elJSON = JSON.parse('{"2025-02-10T19:15Z":"15.10335","2025-02-10T19:30Z":"15.10335","2025-02-10T19:45Z":"15.10335","2025-02-10T20:00Z":"13.46683","2025-02-10T20:15Z":"13.46683","2025-02-10T20:30Z":"13.46683","2025-02-10T20:45Z":"13.46683","2025-02-10T21:00Z":"9.97416","2025-02-10T21:15Z":"9.97416","2025-02-10T21:30Z":"9.97416","2025-02-10T21:45Z":"9.97416","2025-02-10T22:00Z":"7.90341","2025-02-10T22:15Z":"7.90341","2025-02-10T22:30Z":"7.90341","2025-02-10T22:45Z":"7.90341","2025-02-10T23:00Z":"10.26407","2025-02-10T23:15Z":"10.26407","2025-02-10T23:30Z":"10.26407","2025-02-10T23:45Z":"10.26407","2025-02-11T00:00Z":"9.59013","2025-02-11T00:15Z":"9.59013","2025-02-11T00:30Z":"9.59013","2025-02-11T00:45Z":"9.59013","2025-02-11T01:00Z":"8.31505","2025-02-11T01:15Z":"8.31505","2025-02-11T01:30Z":"8.31505","2025-02-11T01:45Z":"8.31505"} ');
  // const elIncrement = 1e-10;
  // const startHours = 1.25;
  // const totalHours = 2;
  // const amongLowest = 1;
  // ==>> i.e. selected here earlier cheaper, ref. price at 1h 15min from now, observe till  ++2hours

try {

  await tag(tagStart + 'elAct', null);
  await tag(tagStart + 'elAve', null);
  await tag(tagStart + 'elMin', null);
  await tag(tagStart + 'elMax', null);
  await tag(tagStart + 'elMinDiff', null);
  await tag(tagStart + 'elMaxDiff', null);
  await tag(tagStart + 'elAveShare', null);
  await tag(tagStart + 'elMinShare', null);
  await tag(tagStart + 'elMaxShare', null);
  await tag(tagStart + 'elPosShare', null);
  await tag(tagStart + 'elPosMin', null);
  await tag(tagStart + 'elPosMax', null);

  const startTime = new Date().getTime() + startHours *(60*60*1000);
    const startTime15min = Math.floor( new Date(startTime) /(15*60*1000)) * (15*60*1000);
    const startDate = new Date (startTime15min).toISOString().slice(0, -8) + "Z";

  const endTime = new Date().getTime() + (startHours+totalHours-0.25) *(60*60*1000);
    const endTime15min = Math.floor( new Date(endTime) /(15*60*1000)) * (15*60*1000);
    const endDate = new Date (endTime15min).toISOString().slice(0, -8) + "Z";


  const elJSONFiltered = Object.fromEntries(
    Object.entries(elJSON).filter(([key, value]) => key >= startDate && key <= endDate)
  );

  let elJSONModified = {};
  let index = 0;
  for (let key in elJSONFiltered) {
    elJSONModified[key] = parseFloat(elJSONFiltered[key]) + index * elIncrement;
    index++;
  }

  let elSortedValues = Object.values(elJSONModified)
    .map(v => parseFloat(v))
    .sort((a, b) => a - b);
  const elPosMin = elSortedValues.indexOf(elJSONModified[startDate]) +1;
  const elMin = elSortedValues[0];

  const elPosShare = ((elPosMin - 0.5 ) / elSortedValues.length)
  const elSum = elSortedValues.reduce((acc, val) => acc + val, 0);
  const elAve = elSum / elSortedValues.length;
  const elAveShare = elJSONModified[startDate] / elAve;
  const elMinShare = elJSONModified[startDate] / elMin;
  const elMinDiff = elJSONModified[startDate] - elMin;

  elSortedValues = Object.values(elJSONModified)
    .map(v => parseFloat(v))
    .sort((a, b) => b - a);
  const elPosMax = elSortedValues.indexOf(parseFloat(elJSONModified[startDate])) +1;
  const elMax = elSortedValues[0];
  const elMaxShare = elJSONModified[startDate] / elMax;
  const elMaxDiff = elJSONModified[startDate] - elMax;


  //  console.log(startDate, ' / ', endDate);

  // console.log('Prices: ', parseFloat(parseFloat(elJSONModified[startDate]).toFixed(2)), parseFloat(elAve.toFixed(2)),parseFloat(elMin.toFixed(2)), parseFloat(elMax.toFixed(2)));
  
  // console.log('Shares: ', parseFloat(elAveShare.toFixed(3)), parseFloat(elMinShare.toFixed(3)), parseFloat(elMaxShare.toFixed(3)), parseFloat(elPosShare.toFixed(3)));

  // console.log('Pos: ', parseFloat(elPosMin), parseFloat(elPosMax));
  // console.log('Diff: ', parseFloat(elMinDiff.toFixed(2)), parseFloat(elMaxDiff.toFixed(2)));


  await tag(tagStart + 'elAct', parseFloat(parseFloat(elJSONModified[startDate]).toFixed(2)));
  await tag(tagStart + 'elAve', parseFloat(elAve.toFixed(2)));
  await tag(tagStart + 'elMin', parseFloat(elMin.toFixed(2)));
  await tag(tagStart + 'elMax', parseFloat(elMax.toFixed(2)));
  await tag(tagStart + 'elMinDiff', parseFloat(elMinDiff.toFixed(2)));
  await tag(tagStart + 'elMaxDiff', parseFloat(elMaxDiff.toFixed(2)));
  await tag(tagStart + 'elAveShare', parseFloat(elAveShare.toFixed(3)));
  await tag(tagStart + 'elMinShare', parseFloat(elMinShare.toFixed(3)));
  await tag(tagStart + 'elMaxShare', parseFloat(elMaxShare.toFixed(3)));
  await tag(tagStart + 'elPosShare', parseFloat(elPosShare.toFixed(3)));
  await tag(tagStart + 'elPosMin', parseFloat(elPosMin));
  await tag(tagStart + 'elPosMax', parseFloat(elPosMax));

  if(amongLowest >= elPosMin) {
    return true;
  } else {
    return false
  }

} catch (error) {

  await tag(tagStart + 'ErrorText: ' + error);
  throw new Error(error)

}

---- edit ----
Nyt koodi myös tulilla (korvasi flown (ja se PbH-kortin) jolla vain osa-optimi (kun näki vaan max. 35h)

eli "onko (15min päässä eli) tuleva 15min jakson sähköhinta sellainen, että on halvempi 160h aikana vs. kallein-15min-päällä-tarve-hinta (


(tarve ka 1h / vrk, mutta useampi päivä voi olla välissä ilman ongelmia…)

1 Like

Olisiko tästä ohjeet heikkotaitoisemmalle saatavilla vai onko muut keksineet jotain? Kesäkuussa alkaa Fortumilla varttisähkö.

Sama Pohjois-Karjalan sähköllä. 12.6. alkaa. Epäilen että koko suomi siirtyy samalla päivämäärällä ja luonnollisesti etunenässä muuhun Eurooppaan nähden. Minulla ei ole ratkaisua ohjaukseen, joten kiinnitin hinnan tarjouksen perusteella, jotta saa rauhassa katsella miten hinnat alkavat muodostumaan.

Hollanti ilmeisesti samaan aikaan. Alkaa PbtH:lla olla vähän paineita. Sopivat lahjoitukset työn tueksi voisi edistää.

1 Like

Saattaa olla pitkän taipaleen päässä toimiva ohjelma.

Minäkin laittelin lahjoitusta PBTH- kehittäjällä, näyttää siellä hiljalleen painetta jatkokehitykseen forumin suunnalta tulevan.