Skip to content

Commit 136cbf6

Browse files
weymannolemr
authored andcommitted
[tibber] Introduce history values (openhab#20699)
* initial commit Signed-off-by: Bernd Weymann <bernd.weymann@gmail.com> Signed-off-by: olemr <olemr@olemr.com>
1 parent 5526f84 commit 136cbf6

17 files changed

Lines changed: 1493 additions & 20 deletions

File tree

bundles/org.openhab.binding.tibber/README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
The Tibber Binding retrieves `prices` from the [Tibber API](https://developer.tibber.com).
44
If you have Tibber Pulse hardware, you can also use the [live group](#live-group) and [statistics group](#statistics-group).
5+
Historical energy data (consumption, cost, and production) is available in the [history group](#history-group) for all users.
56

67
## Supported Things
78

@@ -124,6 +125,49 @@ All values are read-only.
124125
| daily-production | Number:Energy | Net energy produced since midnight in kilowatt-hours |
125126
| last-hour-production | Number:Energy | Net energy produced since last hour shift in kilowatt-hours |
126127

128+
### `history` group
129+
130+
Historical energy consumption, cost, and production delivered as time series.
131+
All values are read-only.
132+
133+
The Tibber API only returns **completed** periods — for example, an annual query run in May 2026 returns data up to end of 2025.
134+
The binding automatically fills the gap for the current incomplete period by aggregating data bottom-up:
135+
daily entries are summed into the current week, weekly entries into the current month, and monthly entries into the current year.
136+
These synthetic entries are transient and will be replaced by real API data once the period closes.
137+
138+
| Channel ID | Type | Description | Time Series |
139+
|---------------------|-----------------|--------------------------------------|-------------|
140+
| yearly-consumption | Number:Energy | Yearly energy consumption | yes |
141+
| yearly-cost | Number:Currency | Yearly energy costs | yes |
142+
| yearly-production | Number:Energy | Yearly energy production | yes |
143+
| monthly-consumption | Number:Energy | Monthly energy consumption | yes |
144+
| monthly-cost | Number:Currency | Monthly energy costs | yes |
145+
| monthly-production | Number:Energy | Monthly energy production | yes |
146+
| weekly-consumption | Number:Energy | Weekly energy consumption | yes |
147+
| weekly-cost | Number:Currency | Weekly energy costs | yes |
148+
| weekly-production | Number:Energy | Weekly energy production | yes |
149+
| daily-consumption | Number:Energy | Daily energy consumption | yes |
150+
| daily-cost | Number:Currency | Daily energy costs | yes |
151+
| daily-production | Number:Energy | Daily energy production | yes |
152+
153+
#### Persistence Requirement
154+
155+
History channels deliver data as time series, which is not supported by the default [rrd4j](https://www.openhab.org/addons/persistence/rrd4j/) persistence.
156+
Items linked to history channels **must** be configured with a persistence service that supports time series, such as [InfluxDB](https://www.openhab.org/addons/persistence/influxdb/) or [InMemory](https://www.openhab.org/addons/persistence/inmemory/).
157+
Without a compatible persistence service, history data will not be stored correctly.
158+
159+
#### Initial Fetch on Item Link
160+
161+
When a new item is linked to a history channel, the binding performs an **initial fetch for one time window only** — for example, the last year for yearly channels or the last month for monthly channels.
162+
This keeps the startup load low and avoids requesting the entire available history from the Tibber API at once.
163+
164+
To populate the full available history (all years, months, weeks, or days), trigger the [`fetchHistory`](#fetchhistory) Thing Action manually after linking the item.
165+
166+
#### Automatic Refresh
167+
168+
Once an item is linked, history data is refreshed automatically once per day at 01:00.
169+
A full re-fetch for any time window can also be triggered at any time via the [`fetchHistory`](#fetchhistory) Thing Action.
170+
127171
## Thing Actions
128172

129173
Thing actions can be used to perform calculations on the currently available price information cached by the binding.
@@ -439,6 +483,32 @@ JSON Object `scheduleEntry`
439483
}
440484
```
441485

486+
### `fetchHistory`
487+
488+
Triggers a full history fetch for a specific time window and stores the result persistently.
489+
Use this action to manually refresh history data, for example after the binding has been offline for an extended period.
490+
491+
#### Parameters
492+
493+
| Name | Type | Description | Required |
494+
|--------|--------|-----------------------------------------------------|----------|
495+
| window | String | Time window to fetch: `ANNUAL`, `MONTHLY`, `WEEKLY`, `DAILY` | yes |
496+
497+
#### Example
498+
499+
```java
500+
rule "Tibber Fetch Full History"
501+
when
502+
System started // use your trigger
503+
then
504+
var actions = getActions("tibber","tibber:tibberapi:xyz")
505+
actions.fetchHistory("ANNUAL")
506+
actions.fetchHistory("MONTHLY")
507+
actions.fetchHistory("WEEKLY")
508+
actions.fetchHistory("DAILY")
509+
end
510+
```
511+
442512
## Full Example
443513

444514
Full example with `demo.things` and `demo.items`
@@ -478,6 +548,19 @@ Number:Energy Tibber_API_Last_Hour_Consumption "Last Hour Consu
478548
Number:Energy Tibber_API_Total_Production "Total Production" {channel="tibber:tibberapi:xyz:statistics#total-production"}
479549
Number:Energy Tibber_API_Daily_Production "Daily Production" {channel="tibber:tibberapi:xyz:statistics#daily-production"}
480550
Number:Energy Tibber_API_Last_Hour_Production "Last Hour Production" {channel="tibber:tibberapi:xyz:statistics#last-hour-production"}
551+
552+
Number:Energy Tibber_API_Yearly_Consumption "Yearly Consumption" {channel="tibber:tibberapi:xyz:history#yearly-consumption"}
553+
Number:Currency Tibber_API_Yearly_Cost "Yearly Cost" {channel="tibber:tibberapi:xyz:history#yearly-cost"}
554+
Number:Energy Tibber_API_Yearly_Production "Yearly Production" {channel="tibber:tibberapi:xyz:history#yearly-production"}
555+
Number:Energy Tibber_API_Monthly_Consumption "Monthly Consumption" {channel="tibber:tibberapi:xyz:history#monthly-consumption"}
556+
Number:Currency Tibber_API_Monthly_Cost "Monthly Cost" {channel="tibber:tibberapi:xyz:history#monthly-cost"}
557+
Number:Energy Tibber_API_Monthly_Production "Monthly Production" {channel="tibber:tibberapi:xyz:history#monthly-production"}
558+
Number:Energy Tibber_API_Weekly_Consumption "Weekly Consumption" {channel="tibber:tibberapi:xyz:history#weekly-consumption"}
559+
Number:Currency Tibber_API_Weekly_Cost "Weekly Cost" {channel="tibber:tibberapi:xyz:history#weekly-cost"}
560+
Number:Energy Tibber_API_Weekly_Production "Weekly Production" {channel="tibber:tibberapi:xyz:history#weekly-production"}
561+
Number:Energy Tibber_API_History_Daily_Consumption "History Daily Consumption" {channel="tibber:tibberapi:xyz:history#daily-consumption"}
562+
Number:Currency Tibber_API_History_Daily_Cost "History Daily Cost" {channel="tibber:tibberapi:xyz:history#daily-cost"}
563+
Number:Energy Tibber_API_History_Daily_Production "History Daily Production" {channel="tibber:tibberapi:xyz:history#daily-production"}
481564
```
482565

483566
### Rule listen to day-ahead price update

bundles/org.openhab.binding.tibber/src/main/java/org/openhab/binding/tibber/internal/TibberBindingConstants.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* @author Stian Kjoglum - Initial contribution
2828
* @author Bernd Weymann - Enhance used constants
2929
* @author Bernd Weymann - Add Map collection for price / cost channels
30+
* @author Bernd Weymann - Add history channel group
3031
*/
3132
@NonNullByDefault
3233
public class TibberBindingConstants {
@@ -44,6 +45,7 @@ public class TibberBindingConstants {
4445
public static final String CHANNEL_GROUP_PRICE = "price";
4546
public static final String CHANNEL_GROUP_LIVE = "live";
4647
public static final String CHANNEL_GROUP_STATISTICS = "statistics";
48+
public static final String CHANNEL_GROUP_HISTORY = "history";
4749

4850
// price channels
4951
public static final String CHANNEL_TOTAL_PRICE = "total";
@@ -79,6 +81,20 @@ public class TibberBindingConstants {
7981
public static final String CHANNEL_DAILY_PRODUCTION = "daily-production";
8082
public static final String CHANNEL_LAST_HOUR_PRODUCTION = "last-hour-production";
8183

84+
// history channels
85+
public static final String CHANNEL_YEARLY_CONSUMPTION = "yearly-consumption";
86+
public static final String CHANNEL_YEARLY_COST = "yearly-cost";
87+
public static final String CHANNEL_YEARLY_PRODUCTION = "yearly-production";
88+
public static final String CHANNEL_MONTHLY_CONSUMPTION = "monthly-consumption";
89+
public static final String CHANNEL_MONTHLY_COST = "monthly-cost";
90+
public static final String CHANNEL_MONTHLY_PRODUCTION = "monthly-production";
91+
public static final String CHANNEL_WEEKLY_CONSUMPTION = "weekly-consumption";
92+
public static final String CHANNEL_WEEKLY_COST = "weekly-cost";
93+
public static final String CHANNEL_WEEKLY_PRODUCTION = "weekly-production";
94+
public static final String CHANNEL_HISTORY_DAILY_CONSUMPTION = "daily-consumption";
95+
public static final String CHANNEL_HISTORY_DAILY_COST = "daily-cost";
96+
public static final String CHANNEL_HISTORY_DAILY_PRODUCTION = "daily-production";
97+
8298
// List of all events
8399
public static final String EVENT_DAY_AHEAD_AVAILABLE = "DAY_AHEAD_AVAILABLE";
84100

@@ -94,6 +110,8 @@ public class TibberBindingConstants {
94110
public static final String PRICE_QUERY_RESOURCE_PATH = "/graphql/prices.graphql";
95111
public static final String REALTIME_QUERY_RESOURCE_PATH = "/graphql/realtime.graphql";
96112
public static final String WEBSOCKET_SUBSCRIPTION_RESOURCE_PATH = "/graphql/websocket.graphql";
113+
public static final String CONSUMPTION_QUERY_RESOURCE_PATH = "/graphql/consumption.graphql";
114+
public static final String PRODUCTION_QUERY_RESOURCE_PATH = "/graphql/production.graphql";
97115

98116
public static final String SCHEDULE_CONTAINER = "{\"size\":%s, \"cost\":%s, \"schedule\":%s}";
99117
public static final String QUERY_CONTAINER = "{\"query\":\"%s\"}";
@@ -102,6 +120,9 @@ public class TibberBindingConstants {
102120
public static final String DISCONNECT_MESSAGE = "{\"type\":\"connection_terminate\",\"payload\":null}";
103121
public static final String SUBSCRIPTION_MESSAGE = "{\"id\":\"1\",\"type\":\"subscribe\",\"payload\":{\"variables\":{},\"extensions\":{},\"operationName\":null,\"query\":\"subscription { liveMeasurement(homeId:\\\"%s\\\") { timestamp power lastMeterConsumption lastMeterProduction accumulatedConsumption accumulatedConsumptionLastHour accumulatedCost currency minPower averagePower maxPower voltagePhase1 voltagePhase2 voltagePhase3 currentL1 currentL2 currentL3 powerProduction accumulatedProduction accumulatedProductionLastHour minPowerProduction maxPowerProduction }}\"}}";
104122

123+
public static final String[] HISTORY_CONSUMPTION_JSON_PATH = { "data", "viewer", "home", "consumption" };
124+
public static final String[] HISTORY_PRODUCTION_JSON_PATH = { "data", "viewer", "home", "production" };
125+
105126
public static final String[] INITIAL_QUERY_JSON_PATH = new String[] { "data", "viewer", "home" };
106127
public static final String[] PRICE_INFO_JSON_PATH = new String[] { "data", "viewer", "home", "currentSubscription",
107128
"priceInfo" };

bundles/org.openhab.binding.tibber/src/main/java/org/openhab/binding/tibber/internal/TibberHandlerFactory.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.openhab.core.i18n.TimeZoneProvider;
2121
import org.openhab.core.io.net.http.HttpClientFactory;
2222
import org.openhab.core.scheduler.CronScheduler;
23+
import org.openhab.core.storage.StorageService;
2324
import org.openhab.core.thing.Thing;
2425
import org.openhab.core.thing.ThingTypeUID;
2526
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
@@ -35,20 +36,24 @@
3536
*
3637
* @author Stian Kjoglum - Initial contribution
3738
* @author Bernd Weymann - Use HttpClientFactory, CronScheduler and TimeZoneProvider
39+
* @author Bernd Weymann - Add StorageService for history persistence
40+
* @author Bernd Weymann - Add history channel group
3841
*/
3942
@NonNullByDefault
4043
@Component(configurationPid = "binding.tibber", service = ThingHandlerFactory.class)
4144
public class TibberHandlerFactory extends BaseThingHandlerFactory {
4245
private final HttpClientFactory httpFactory;
4346
private final CronScheduler cron;
4447
private final TimeZoneProvider timeZoneProvider;
48+
private final StorageService storageService;
4549

4650
@Activate
4751
public TibberHandlerFactory(final @Reference HttpClientFactory httpFactory, final @Reference CronScheduler cron,
48-
final @Reference TimeZoneProvider timeZoneProvider) {
52+
final @Reference TimeZoneProvider timeZoneProvider, final @Reference StorageService storageService) {
4953
this.httpFactory = httpFactory;
5054
this.cron = cron;
5155
this.timeZoneProvider = timeZoneProvider;
56+
this.storageService = storageService;
5257
}
5358

5459
@Override
@@ -60,7 +65,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
6065
protected @Nullable ThingHandler createHandler(Thing thing) {
6166
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
6267
if (thingTypeUID.equals(TIBBER_THING_TYPE)) {
63-
return new TibberHandler(thing, httpFactory.getCommonHttpClient(), cron, bundleContext, timeZoneProvider);
68+
return new TibberHandler(thing, httpFactory.getCommonHttpClient(), cron, bundleContext, timeZoneProvider,
69+
storageService);
6470
} else {
6571
return null;
6672
}

bundles/org.openhab.binding.tibber/src/main/java/org/openhab/binding/tibber/internal/action/TibberActions.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.openhab.binding.tibber.internal.exception.CalculationParameterException;
3131
import org.openhab.binding.tibber.internal.exception.PriceCalculationException;
3232
import org.openhab.binding.tibber.internal.handler.TibberHandler;
33+
import org.openhab.binding.tibber.internal.history.TibberHistory;
3334
import org.openhab.core.automation.annotation.ActionInput;
3435
import org.openhab.core.automation.annotation.ActionOutput;
3536
import org.openhab.core.automation.annotation.RuleAction;
@@ -219,6 +220,26 @@ public class TibberActions implements ThingActions {
219220
}
220221
}
221222

223+
@RuleAction(label = "@text/actionFetchHistoryLabel", description = "@text/actionFetchHistoryDescription")
224+
public void fetchHistory(
225+
@ActionInput(name = "window", label = "@text/actionInputWindowLabel", type = "java.lang.String") String window) {
226+
TibberHandler thingHandler = this.thingHandler;
227+
if (thingHandler == null) {
228+
logger.warn("No Thing attached to Actions! Maybe OFFLINE or Thing deactivated.");
229+
return;
230+
}
231+
try {
232+
TibberHistory.TimeWindow timeWindow = TibberHistory.TimeWindow.valueOf(window.toUpperCase());
233+
thingHandler.fetchHistory(timeWindow);
234+
} catch (IllegalArgumentException e) {
235+
logger.warn("Unknown history window '{}'. Valid values: ANNUAL, MONTHLY, WEEKLY, DAILY", window);
236+
}
237+
}
238+
239+
public static void fetchHistory(ThingActions actions, String window) {
240+
((TibberActions) actions).fetchHistory(window);
241+
}
242+
222243
public static Instant priceInfoStart(ThingActions actions) {
223244
return ((TibberActions) actions).priceInfoStart();
224245
}

0 commit comments

Comments
 (0)