Control a water heater and ground source heat pump based on cheap hours of spot priced electricity

Try this as the latter part (again coding on the fly without possibility to test this)

// The rest of the day does not have any control points yet.
// Read the spot prices from the database, allow 7.30-8.00 and block all the rest.
start = time.toZDT('07:30').plusDays(1);
stop = time.toZDT('00:00').plusDays(2);
prices = influx.getPoints('SpotPrice', start, stop);
optimizer.setPrices(prices);

// Force charging on for cabin heating
cabinHeatingStart = time.toZDT('07:30').plusDays(1);
cabinHeatingDuration = time.Duration.parse('PT30M');
optimizer.setControlForPeriod(cabinHeatingStart, cabinHeatingDuration, 1);

// Block all the rest
optimizer.blockAllRemaining();
points = optimizer.getControlPoints();
influx.writePoints('CarChargingControl', points);

This works. Thanks.

1 Like

Is there a typo here? Shouldn´t there also be a

carChargingForcedOff

item?

Also, the forced charging only sets one hour forced on. I have the rule to to be triggered when

carChargingForcedOn

changes, but the rule only runs once, for the forced on start hour.

How should the rule be set to run, so that the forced hours would be set between forced on and forced off?

I don’t know what you are referring with ‘here’ so hard to comment.

This is expected behavior. The point of these manual force on and force off rules is that if you are not happy what the automatically determined results are, you can manually force a given hour ON or OFF. One hour at a time.

Example:

  • Let’s say that the cheapest 2 hours are in the afternoon between 15 and 17.
  • But you want your device to be ON in the night between 03 and 05.
  • So you can use the “force off” by first selecting 15:00 and then 16:00. These two operations will set control points with 0 values for the hour starting at 15:00 and the second update will set control points with 0 values for the hour starting at 16:00
  • And then you can use the “force on” by first selecting 03:00 and then updating the value to 04:00. The first update will set control points with value 1 for the hour starting at 03:00 and the second update will set control points with value 1 for the hour starting at 04:00

For some reason my quote.did not work. I was referring to #325

Thanks for the clarification. I had misunderstood how the manual setting works.

Hi

Can you read planned pre co ditioning time from the cars cloud service, thought that coul be a nice solution if you could and then start the charger a minute before or so. I just let our charger keep 6amps output when completed, but that depends on how your car reacts in combination with charger and where you set target SoC. I pretty much allways charge to 100% and then my charger sets state to completed, if I set a lower state of charge in the cars app it allso ends up as completed, but if I set a lower soc in openHAB it won’t.

Unfortunatle I do not have any integrated cloudservice for my evs anymore I had when I was rmdrivin KIA but now I have a Tesla and a BYD but havent got to registering developer api for Tesla and BYD dosen’t seems to have a usfully api. I would like to make an easy OBD dongle that sends data from car directly to an selfhostef MQTT btoker, but time and skills aren’t on my side.

Hello,

  1. What is wrong when I try to use tariff-calculator.js to calculate night/day transfer prices, but I get the following error?

43 while (current < stop) {

2024-08-08 11:43:50.910 [INFO ] [automation.script.ui.FetchSpotPrices] - tariff-calculator.js: Calculating distribution prices…
2024-08-08 11:43:50.912 [ERROR] [on.script.javascript.FetchSpotPrices] - Failed to execute script: TypeError: A conversion from Temporal to a number is not allowed. To compare use the methods .equals(), .compareTo(), .isBefore() or one that is more suitable to your use case.
at .:anonymous(@openhab-globals.js:2)
at .getPrices(/etc/openhab/automation/js/node_modules/openhab-spot-price-optimizer/tariff-calculator.js:43)
at .:program(:18)
at org.graalvm.polyglot.Context.eval(Context.java:399)
at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458)
… 89 more
2024-08-08 11:43:50.912 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID ‘FetchSpotPrices’ failed: org.graalvm.polyglot.PolyglotException: TypeError: A conversion from Temporal to a number is not allowed. To compare use the methods .equals(), .compareTo(), .isBefore() or one that is more suitable to your use case.

  1. Should I then also use TotalPrice instead of SpotPrice in the Rules so that all optimizations are done according to TotalPrice instead of SpotPrice?

BR, Jakke

@Jak Please provide the code of your rule where you are calling the tariff calculator.

Hello,

Here is the rule (it is almost copy paste from your instructions).

// Load modules. Influx connection parameters must be configured in config.js
Influx = require(‘openhab-spot-price-optimizer/influx.js’);
TariffCalculator = require(‘openhab-spot-price-optimizer/tariff-calculator.js’);

// Create objects.
influx = new Influx.Influx();
tariffCalculator = new TariffCalculator.TariffCalculator();

// Calculate distribution fees from yesterday 00:00 to day after tomorrow 00:00.
start = time.toZDT(‘00:00’).minusDays(1);
end = time.toZDT(‘00:00’).plusDays(2);

// price1: 3.87 siirto päivä (1.9.2024->)
// price2: 1.31 siirto yö (1.9.2024->)
// tax: 0.41 + 2.827515 = 3.24 välityspalkkio + sähkövero
priceParams = {
‘price1’: 3.87,
‘price2’: 1.31,
‘tax’: 3.24
};
//distributionPrices = tariffCalculator.getPrices(start, end, ‘seasonal’, priceParams);
distributionPrices = tariffCalculator.getPrices(start, end, ‘night’, priceParams);
influx.writePoints(‘DistributionPrice’, distributionPrices);

// Calculate TotalPrices
spotPrices = influx.getPoints(‘SpotPrice’, start, end);
distributionPrices = influx.getPoints(‘DistributionPrice’, start, end);
totalPrices = tariffCalculator.getTotalPrices(spotPrices, distributionPrices);
influx.writePoints(‘TotalPrice’, totalPrices);

hmm…maybe I need to adjust start and end?

BR, Jakke

The only difference seems to be that you are using night transfer and I’m using seasonal transfer. Your rule looks good to me at first glance, so I’m suspecting there is a bug in the night transfer calculator in my library. I’ll check if I can find some time during this weekend to debug it.

Thanks for reporting this!

Cheers,
Markus

Nggh, found it by looking at the code. I have inconsistent ways to pass the price parameters between the seasonal transfer and night transfer. Here are the method signatures:

    /**
     * Returns price for given hour using Night Distribution.
     *
     * @param ZonedDateTime zdt
     *   Hour to calculate the tariff for (ensure input is a full hour).
     * @param object priceParams
     *   Object with the following keys: price1, price2, tax
     *
     * @return float
     *   Tariff c/kWh at the given time.
     */
    calculatePriceNightDistribution(zdt, priceParams)

And

    /**
     * Returns price for given hour using Seasonal Distribution.
     *
     * @param ZonedDateTime zdt
     *   Hour to calculate the tariff for (ensure input is a full hour).
     * @param object priceParams
     *   Object with the following keys: price1, price2, tax
     *
     * @return float
     *   Tariff c/kWh at the given time.
     */
    calculatePriceSeasonalDistribution(zdt, price1, price2, tax)

So even though the documentation block for the calculatePriceSeasonalDistribution says that the price components should be provided as an object with price1, price2 and tax, the actual implementation is done so that these are passed as individual variables.

Whereas in the calculatePriceNightDistribution these price components are passed as an object instead of individual variables.

I’ll have to look at this during this weekend and figure out a way to do this without breaking backwards compatibility with existing users.

Cheers,
Markus

Hello,

Thank you for looking into this. Even if I change the rule night->seasonal, I get pretty much the same error message…

BR, Jakke

It looks like Entso-E might have changed their XML response when fetching the spot prices for multiple days. The current version of openhab-spot-price-optimizer is not able to parse the response and prints the following error message to the log

Unexpected Publication_MarketDocument response!

I’m currently investigating what has changed. If you’re only fetching spot prices for one day, everything works normally. In other words, if you are experiencing this issue, modify your Rule script so that you’re only fetching the prices for one day.

Cheers,
Markus

What would be the correct setting in the script?

start = time.toZDT(‘00:00’).plusDays(1);
end = time.toZDT(‘00:00’).plusDays(2);

This works only in the afternoon after the prices have been published.

The tutorial has this, which tries to fetch past two days and tomorrow, but this doesn’t work at the moment.

start = time.toZDT(‘00:00’).minusDays(1);
end = time.toZDT(‘00:00’).plusDays(2);

1 Like

Okay, found out the difference.

Previously, the Entso-E API behaved so that when multiple days was requested, the response structure was like below. There was a TimeSeries element for each day and the TimeSeries contained one Period element. So like this.

<Publication_MarketDocument>
  <TimeSeries>
    <Period>
      // Data for day 1
    </Period>
  </TimeSeries>
  <TimeSeries>
    <Period>
      // Data for day 2
    </Period>
  </TimeSeries>
</Publication_MarketDocument>

Now, the response structure looks like like below. There is only one TimeSeries element, but that now contains multiple Period elements, one for each day. So like this.

<Publication_MarketDocument>
  <TimeSeries>
    <Period>
      // Data for day 1
    </Period>
    <Period>
      // Data for day 2
    </Period>
  </TimeSeries>
</Publication_MarketDocument>

I have reached out to Entso-E to ask if this change is intentional or are they planning to revert the change. So I’m not publishing a new release of openHAB Spot Price Optimizer yet before I get an answer. Modify your Rule actions so that you will be requesting only 1 day worth of prices and ensure that you execute the script on the afternoon side of the day when the day-ahead prices have been published.

Cheers,
Markus

1 Like

Okay just heads up… I improved the price fetching logic so that it can now handle 1-N TimeSeries elements, each consisting of 1-N Period elements.

My new kick-ass level heating algorithm is also ready. I will add just a little bit more unit testing for this 1-N logic and will then cut a new release. This will be most probably tomorrow because we have some family stuff today.

Stay tuned!

Cheers,
Markus

1 Like

Hello

Will the future version also fix the calculation of nightly and seasonal transfer fees?

BR, Jakke

Yeah