Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tibber] Add time series support for Tibber prices #16275

Merged
merged 12 commits into from
Jan 15, 2024
79 changes: 42 additions & 37 deletions bundles/org.openhab.binding.tibber/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,47 @@ The channels (i.e. measurements) associated with the Binding:

Tibber Default:

| Channel ID | Description | Read-only |
|--------------------|---------------------------------------------------------|-----------|
| Current Total | Current Total Price (energy + tax) | True |
| Starts At | Current Price Timestamp | True |
| Current Level | Current Price Level | True |
| Daily Cost | Daily Cost (last/previous day) | True |
| Daily Consumption | Daily Consumption (last/previous day) | True |
| Daily From | Timestamp (daily from) | True |
| Daily To | Timestamp (daily to) | True |
| Hourly Cost | Hourly Cost (last/previous hour) | True |
| Hourly Consumption | Hourly Consumption (last/previous hour) | True |
| Hourly From | Timestamp (hourly from) | True |
| Hourly To | Timestamp (hourly to) | True |
| Tomorrow prices | JSON array of tomorrow's prices. See below for example. | True |
| Today prices | JSON array of today's prices. See below for example. | True |
| Channel ID | Description | Read-only | Forecast |
|----------------------|---------------------------------------------------------|-----------|----------|
| current_total | Current Total Price (energy + tax) | True | yes |
| current_startsAt | Current Price Timestamp | True | no |
| current_level | Current Price Level | True | no |
| daily_cost | Daily Cost (last/previous day) | True | no |
| daily_consumption | Daily Consumption (last/previous day) | True | no |
| daily_from | Timestamp (daily from) | True | no |
| daily_to | Timestamp (daily to) | True | no |
| hourly_cost | Hourly Cost (last/previous hour) | True | no |
| hourly_consumption | Hourly Consumption (last/previous hour) | True | no |
| hourly_from | Timestamp (hourly from) | True | no |
| hourly_to | Timestamp (hourly to) | True | no |
| tomorrow_prices | JSON array of tomorrow's prices. See below for example. | True | no |
| today_prices | JSON array of today's prices. See below for example. | True | no |

Tibber Pulse (optional):

| Channel ID | Description | Read-only |
|-------------------------|------------------------------------------|-----------|
| Timestamp | Timestamp for live measurements | True |
| Power | Live Power Consumption | True |
| Last Meter Consumption | Last Recorded Meter Consumption | True |
| Accumulated Consumption | Accumulated Consumption since Midnight | True |
| Accumulated Cost | Accumulated Cost since Midnight | True |
| Accumulated Reward | Accumulated Reward since Midnight | True |
| Currency | Currency of Cost | True |
| Min Power | Min Power Consumption since Midnight | True |
| Average Power | Average Power Consumption since Midnight | True |
| Max Power | Max Power Consumption since Midnight | True |
| Voltage 1-3 | Voltage per Phase | True |
| Current 1-3 | Current per Phase | True |
| Power Production | Live Power Production | True |
| Accumulated Production | Accumulated Production since Midnight | True |
| Last Meter Production | Last Recorded Meter Production | True |
| Min Power Production | Min Power Production since Midnight | True |
| Max Power Production | Max Power Production since Midnight | True |
| Channel ID | Description | Read-only |
|-----------------------------|------------------------------------------|-----------|
| live_timestamp | Timestamp for live measurements | True |
| live_power | Live Power Consumption | True |
| live_lastMeterConsumption | Last Recorded Meter Consumption | True |
| live_accumulatedConsumption | Accumulated Consumption since Midnight | True |
| live_accumulatedCost | Accumulated Cost since Midnight | True |
| live_accumulatedReward | Accumulated Reward since Midnight | True |
| live_currency | Currency of Cost | True |
| live_minPower | Min Power Consumption since Midnight | True |
| live_averagePower | Average Power Consumption since Midnight | True |
| live_maxPower | Max Power Consumption since Midnight | True |
| live_voltage1 | Voltage Phase 1 | True |
| live_voltage2 | Voltage Phase 2 | True |
| live_voltage3 | Voltage Phase 3 | True |
| live_current1 | Current Phase 1 | True |
| live_current2 | Current Phase 2 | True |
| live_current3 | Current Phase 3 | True |
| live_powerProduction | Live Power Production | True |
| live_accumulatedProduction | Accumulated Production since Midnight | True |
| live_lastMeterProduction | Last Recorded Meter Production | True |
| live_minPowerproduction | Min Power Production since Midnight | True |
| live_maxPowerproduction | Max Power Production since Midnight | True |

## Binding Configuration

Expand All @@ -77,7 +81,7 @@ Note: Tibber HomeId is retrieved from [developer.tibber.com](https://developer.t
- If Tibber Pulse is connected, the Tibber API Explorer will report "true" for "realTimeConsumptionEnabled"
- Copy HomeId from Tibber API Explorer, without quotation marks, and use this in the bindings configuration.

```json
```
soenkekueper marked this conversation as resolved.
Show resolved Hide resolved
{
viewer {
homes {
Expand All @@ -102,6 +106,7 @@ Tibber API will be auto discovered if provided input is correct.

## Tomorrow and Today Prices

The today and tomorrow prices are served as forecast on the `current_total` channel and as JSON data on the channels `today_prices` and `tomorrow_prices`.
Example of tomorrow and today prices data structure - an array of tuples:

```json
Expand Down Expand Up @@ -209,13 +214,13 @@ Example of tomorrow and today prices data structure - an array of tuples:

### demo.things

```java
```
soenkekueper marked this conversation as resolved.
Show resolved Hide resolved
Thing tibber:tibberapi:7cfae492 [ homeid="xxx", token="xxxxxxx" ]
```

### demo.items:

```java
```
soenkekueper marked this conversation as resolved.
Show resolved Hide resolved
Number:Dimensionless TibberAPICurrentTotal "Current Total Price [%.2f NOK]" {channel="tibber:tibberapi:7cfae492:current_total"}
DateTime TibberAPICurrentStartsAt "Timestamp - Current Price" {channel="tibber:tibberapi:7cfae492:current_startsAt"}
String TibberAPICurrentLevel "Price Level" {channel="tibber:tibberapi:7cfae492:current_level"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Properties;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
Expand Down Expand Up @@ -52,6 +55,7 @@
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.TimeSeries;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -203,6 +207,10 @@ public void getURLInput(String url) throws IOException {
.getAsJsonObject("home").getAsJsonObject("currentSubscription").getAsJsonObject("priceInfo")
.getAsJsonArray("today");
updateState(TODAY_PRICES, new StringType(today.toString()));

TimeSeries timeSeries = buildTimeSeries(today, tomorrow);
sendTimeSeries(CURRENT_TOTAL, timeSeries);

} catch (JsonSyntaxException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Error communicating with Tibber API: " + e.getMessage());
Expand Down Expand Up @@ -267,6 +275,36 @@ public void getURLInput(String url) throws IOException {
}
}

/**
* Builds the {@link TimeSeries} that represents the future tibber prices.
*
* @param today The prices for today
* @param tomorrow The prices for tomorrow.
* @return The {@link TimeSeries} with future values.
*/
private TimeSeries buildTimeSeries(JsonArray today, JsonArray tomorrow) {
final TimeSeries timeSeries = new TimeSeries(TimeSeries.Policy.REPLACE);
mapTimeSeriesEntries(today, timeSeries);
mapTimeSeriesEntries(tomorrow, timeSeries);
return timeSeries;
}

private static void mapTimeSeriesEntries(JsonArray today, TimeSeries timeSeries) {
soenkekueper marked this conversation as resolved.
Show resolved Hide resolved
for (JsonElement entry : today) {
final Instant startsAt = parseDateTime(entry.getAsJsonObject().get("startsAt"));
final Instant endsAt = startsAt.plus(1, ChronoUnit.HOURS).minus(1, ChronoUnit.MICROS);
final DecimalType value = new DecimalType(entry.getAsJsonObject().get("total").getAsString());
// As the value is valid for exactly one hour and is not linear, add one entry at the beginning
// and one at the end of the interval, so it's a step function instead a linear one.
timeSeries.add(startsAt, value);
timeSeries.add(endsAt, value);
}
}

private static Instant parseDateTime(JsonElement element) {
return ZonedDateTime.parse(element.getAsString()).toInstant();
soenkekueper marked this conversation as resolved.
Show resolved Hide resolved
}

public void startRefresh(int refresh) {
if (pollingJob == null) {
pollingJob = scheduler.scheduleWithFixedDelay(() -> {
Expand Down