Skip to content

Commit 5effac5

Browse files
authored
[energyforecast] Initial contribution (#20325)
* initial version Signed-off-by: Bernd Weymann <bernd.weymann@gmail.com>
1 parent 6ac2780 commit 5effac5

21 files changed

Lines changed: 2317 additions & 0 deletions

File tree

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
/bundles/org.openhab.binding.emotiva/ @espenaf
113113
/bundles/org.openhab.binding.energenie/ @hmerk
114114
/bundles/org.openhab.binding.energidataservice/ @jlaur
115+
/bundles/org.openhab.binding.energyforecast/ @weymann
115116
/bundles/org.openhab.binding.enigma2/ @gdolfen
116117
/bundles/org.openhab.binding.enocean/ @fruggy83
117118
/bundles/org.openhab.binding.enphase/ @Hilbrand

bom/openhab-addons/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,11 @@
546546
<artifactId>org.openhab.binding.energidataservice</artifactId>
547547
<version>${project.version}</version>
548548
</dependency>
549+
<dependency>
550+
<groupId>org.openhab.addons.bundles</groupId>
551+
<artifactId>org.openhab.binding.energyforecast</artifactId>
552+
<version>${project.version}</version>
553+
</dependency>
549554
<dependency>
550555
<groupId>org.openhab.addons.bundles</groupId>
551556
<artifactId>org.openhab.binding.enigma2</artifactId>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This content is produced and maintained by the openHAB project.
2+
3+
* Project home: https://www.openhab.org
4+
5+
== Declared Project Licenses
6+
7+
This program and the accompanying materials are made available under the terms
8+
of the Eclipse Public License 2.0 which is available at
9+
https://www.eclipse.org/legal/epl-2.0/.
10+
11+
== Source Code
12+
13+
https://github.com/openhab/openhab-addons
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Energy Forecast Binding
2+
3+
Binding provides AI price forecast 48h / 96h beyond day-ahead pricing with [Energy Forecast Service](https://www.energyforecast.de/).
4+
Check in beforehand if your [price zone](https://www.energyforecast.de/api-docs/index.html) is supported.
5+
[Registration](https://www.energyforecast.de/users/sign_up) is mandatory!
6+
7+
## Binding Configuration
8+
9+
### `price-forecast` Thing Configuration
10+
11+
| Name | Type | Description | Default | Required |
12+
|-------------------|-----------|-----------------------------------------------------------------------------------|-----------|----------|
13+
| token | text | Token for energy forecast service to provide forecast data | N/A | yes |
14+
| zone | text | Bidding zone for price queries | N/A | yes |
15+
| fixCost | decimal | Net fix costs in ct/kWh added on top of the forecast price, e.g. 15,3 | 0 | no |
16+
| resolution | text | Resolution in ISO 8601 Duration format | PT15M | no |
17+
| refreshInterval | integer | Refresh interval in minutes. Check with service throttling | 180 | no |
18+
| errorLimit | integer | Limit error percentage values for better visualization | 0 | no |
19+
20+
`token` needs to be generated after registration at [Energy Forecast Service](https://www.energyforecast.de/api_keys).
21+
`zone` from [API](https://www.energyforecast.de/api-docs/index.html) are given as options.
22+
`resolution` time resolution given as options. `PT15M` an `PT60M` are supported.
23+
`refreshInterval` given in minutes. Align this value with your [booked plan](https://www.energyforecast.de/pricing).
24+
`errorLimit` to avoid extraordinary error percentage values in `metric` group. Value `0` is no limit.
25+
26+
#### Calculate Gross Price
27+
28+
`fixCost` shall be net costs which will be added to the net energy price.
29+
If you've already one or more items holding fix cost values consider to [calculate the future prices in a rule](https://www.openhab.org/addons/bindings/energidataservice/#time-series).
30+
Use [VAT Transformation Service](https://www.openhab.org/addons/transformations/vat/) to calculate the gross price.
31+
32+
## Channels
33+
34+
All channels delivering `timeseries` information.
35+
Attaching items which are bound only to `rrd4j` persistence will not work.
36+
If you don't have a database installed [InMemory persistence](https://www.openhab.org/addons/persistence/inmemory/) can be used.
37+
38+
### Group `price`
39+
40+
| Channel | Type | Description |
41+
|---------------|-----------------------|---------------------------------------------------|
42+
| series | Number:EnergyPrice | Actual and future price series |
43+
| origin | Number | Originator of the price (market or forecast) |
44+
45+
`series` delivers price information from 48h up to 96h into the future depending on your [booked plan](https://www.energyforecast.de/pricing).
46+
`origin` shows for every price the originator.
47+
48+
- 0: Market
49+
- 1: AI Forecast
50+
51+
### Group `metric`
52+
53+
Metrics for AI price forecasts.
54+
Calculation is done on net prices without configured `fixCost` and any VAT applied via transformations/rules.
55+
See [Future Forecasting](https://www.future-forecasting.de/en/wiki/fehlermass/) for further description.
56+
57+
**Note: After first installation these values will stay empty up to 1,5 days!**
58+
To compare market and forecast prices this time is needed until a market price is available.
59+
60+
| Channel | Type | Description |
61+
|-------------------|-----------------------|-----------------------------------------------------------------------------------|
62+
| forecast | Number:EnergyPrice | AI forecast price series without any market prices |
63+
| forecast-error | Number:EnergyPrice | Difference between market and forecast price |
64+
| percent-error | Number:Dimensionless | Percentage error between market and forecast price |
65+
| mean-abs | Number:EnergyPrice | Mean absolute error showing the average of absolute forecast errors |
66+
| mean-abs-percent | Number:Dimensionless | Mean absolute percentage error showing the average of absolute percentage errors |
67+
68+
`forecast` timeseries contains only forecast prices, market prices are excluded.
69+
`forecast-error` price difference between market and forecast price.
70+
`percent-error` is the `forecast-error` in percent.
71+
It can show extraordinary high values if market prices are around zero cost.
72+
If market price is 0.001 and forecast was 0.006 the percentage error is _high_ while `forecast-error` is quite low.
73+
For visualization you can limit these values with configuration `errorLimit`.
74+
75+
`mean-abs` shows the average of all absolute `forecast-error` calculations as one value.
76+
`mean-abs-percent` shows the average of all absolute `percent-error` calculations as one value.
77+
78+
## Full Example
79+
80+
### `demo.things`
81+
82+
```java
83+
Thing energyforecast:price-forecast:UID "Energy Forecast" [zone="YOUR_BIDDING_ZONE", token="YOUR_TOKEN", fixCost=12.3, resolution="PT15M", refreshInterval=180, errorLimit=0]
84+
```
85+
86+
### `demo.items`
87+
88+
```java
89+
Number:EnergyPrice Energy_Forecast_Price_Series "Price Series" {channel="energyforecast:price-forecast:UID:price#series"}
90+
Number Energy_Forecast_Price_Origins "Price Origin" {channel="energyforecast:price-forecast:UID:price#origin"}
91+
92+
Number:EnergyPrice Energy_Forecast_Forecast "Forecast" {channel="energyforecast:price-forecast:UID:metric#forecast"}
93+
Number:EnergyPrice Energy_Forecast_Forecast_Error "Forecast Error" {channel="energyforecast:price-forecast:UID:metric#forecast-error"}
94+
Number:Dimensionless Energy_Forecast_Percent_Error "Percent Error" {channel="energyforecast:price-forecast:UID:metric#percent-error"}
95+
Number:EnergyPrice Energy_Forecast_Mean_Absolute "Mean Absolute" {channel="energyforecast:price-forecast:UID:metric#mean-abs"}
96+
Number:Dimensionless Energy_Forecast_Mean_Absolute_Percent "Mean Absolute Percent" {channel="energyforecast:price-forecast:UID:metric#mean-abs-percent"}
97+
```
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.openhab.addons.bundles</groupId>
9+
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
10+
<version>5.2.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>org.openhab.binding.energyforecast</artifactId>
14+
15+
<name>openHAB Add-ons :: Bundles :: EnergyForecast Binding</name>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>org.json</groupId>
20+
<artifactId>json</artifactId>
21+
<version>${json.version}</version>
22+
<scope>compile</scope>
23+
</dependency>
24+
</dependencies>
25+
</project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<features name="org.openhab.binding.energyforecast-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.6.0">
3+
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
4+
5+
<feature name="openhab-binding-energyforecast" description="EnergyForecast Binding" version="${project.version}">
6+
<feature>openhab-runtime-base</feature>
7+
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.energyforecast/${project.version}</bundle>
8+
</feature>
9+
</features>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.energyforecast.internal;
14+
15+
import java.time.Clock;
16+
import java.util.Set;
17+
18+
import org.eclipse.jdt.annotation.NonNullByDefault;
19+
import org.openhab.core.thing.ThingTypeUID;
20+
21+
/**
22+
* The {@link EnergyForecastBindingConstants} class defines common constants, which are
23+
* used across the whole binding.
24+
*
25+
* @author Bernd Weymann - Initial contribution
26+
*/
27+
@NonNullByDefault
28+
public class EnergyForecastBindingConstants {
29+
30+
public static final String BINDING_ID = "energyforecast";
31+
32+
// List of all Thing Type UIDs
33+
public static final ThingTypeUID THING_TYPE_PRICE_FORECAST = new ThingTypeUID(BINDING_ID, "price-forecast");
34+
35+
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_PRICE_FORECAST);
36+
37+
public static final String ENERGY_FORECAST_URL = "https://www.energyforecast.de/api/v1/predictions/next_96_hours";
38+
39+
public static final String CHANNEL_GROUP_PRICE = "price";
40+
public static final String CHANNEL_GROUP_METRIC = "metric";
41+
42+
public static final String CHANNEL_PRICE_SERIES = "series";
43+
public static final String CHANNEL_PRICE_ORIGIN = "origin";
44+
45+
public static final String CHANNEL_METRIC_FORECAST = "forecast";
46+
public static final String CHANNEL_METRIC_FORECAST_ERROR = "forecast-error";
47+
public static final String CHANNEL_METRIC_PERCENT_ERROR = "percent-error";
48+
public static final String CHANNEL_METRIC_MAE = "mean-abs";
49+
public static final String CHANNEL_METRIC_MAPE = "mean-abs-percent";
50+
51+
public static Clock clock = Clock.systemDefaultZone();
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.energyforecast.internal;
14+
15+
import static org.openhab.binding.energyforecast.internal.EnergyForecastBindingConstants.*;
16+
17+
import org.eclipse.jdt.annotation.NonNullByDefault;
18+
import org.eclipse.jdt.annotation.Nullable;
19+
import org.openhab.binding.energyforecast.internal.handler.EnergyForecastHandler;
20+
import org.openhab.core.i18n.TimeZoneProvider;
21+
import org.openhab.core.io.net.http.HttpClientFactory;
22+
import org.openhab.core.storage.StorageService;
23+
import org.openhab.core.thing.Thing;
24+
import org.openhab.core.thing.ThingTypeUID;
25+
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
26+
import org.openhab.core.thing.binding.ThingHandler;
27+
import org.openhab.core.thing.binding.ThingHandlerFactory;
28+
import org.osgi.service.component.annotations.Activate;
29+
import org.osgi.service.component.annotations.Component;
30+
import org.osgi.service.component.annotations.Reference;
31+
32+
/**
33+
* The {@link EnergyForecastHandlerFactory} is responsible for creating things and thing
34+
* handlers.
35+
*
36+
* @author Bernd Weymann - Initial contribution
37+
*/
38+
@NonNullByDefault
39+
@Component(configurationPid = "binding.energyforecast", service = ThingHandlerFactory.class)
40+
public class EnergyForecastHandlerFactory extends BaseThingHandlerFactory {
41+
42+
private final StorageService storageService;
43+
private final HttpClientFactory hcf;
44+
private final TimeZoneProvider tzp;
45+
46+
@Activate
47+
public EnergyForecastHandlerFactory(final @Reference HttpClientFactory hcf,
48+
final @Reference StorageService storageService, final @Reference TimeZoneProvider tzp) {
49+
this.hcf = hcf;
50+
this.storageService = storageService;
51+
this.tzp = tzp;
52+
}
53+
54+
@Override
55+
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
56+
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
57+
}
58+
59+
@Override
60+
protected @Nullable ThingHandler createHandler(Thing thing) {
61+
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
62+
63+
if (THING_TYPE_PRICE_FORECAST.equals(thingTypeUID)) {
64+
return new EnergyForecastHandler(thing, hcf.getCommonHttpClient(), storageService, tzp);
65+
}
66+
67+
return null;
68+
}
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.energyforecast.internal.config;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
17+
/**
18+
* The {@link EnergyForecastConfiguration} class contains fields mapping thing configuration parameters.
19+
*
20+
* @author Bernd Weymann - Initial contribution
21+
*/
22+
@NonNullByDefault
23+
public class EnergyForecastConfiguration {
24+
25+
public String zone = "";
26+
public String token = "";
27+
public double fixCost = 0.0;
28+
public String resolution = "PT15M";
29+
public int refreshInterval = 180;
30+
public int errorLimit = 0;
31+
}

0 commit comments

Comments
 (0)