2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.tibber.internal.handler;
15 import static org.openhab.binding.tibber.internal.TibberBindingConstants.*;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.math.BigDecimal;
21 import java.net.URISyntaxException;
22 import java.time.Instant;
23 import java.time.ZonedDateTime;
24 import java.time.format.DateTimeParseException;
25 import java.util.Properties;
26 import java.util.concurrent.Future;
27 import java.util.concurrent.ScheduledFuture;
28 import java.util.concurrent.TimeUnit;
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.eclipse.jetty.client.HttpClient;
33 import org.eclipse.jetty.http.HttpHeader;
34 import org.eclipse.jetty.util.ssl.SslContextFactory;
35 import org.eclipse.jetty.websocket.api.Session;
36 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
37 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
38 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
39 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
40 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
41 import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
42 import org.eclipse.jetty.websocket.client.WebSocketClient;
43 import org.openhab.binding.tibber.internal.config.TibberConfiguration;
44 import org.openhab.core.io.net.http.HttpUtil;
45 import org.openhab.core.library.types.DateTimeType;
46 import org.openhab.core.library.types.DecimalType;
47 import org.openhab.core.library.types.QuantityType;
48 import org.openhab.core.library.types.StringType;
49 import org.openhab.core.library.unit.Units;
50 import org.openhab.core.thing.Channel;
51 import org.openhab.core.thing.ChannelUID;
52 import org.openhab.core.thing.Thing;
53 import org.openhab.core.thing.ThingStatus;
54 import org.openhab.core.thing.ThingStatusDetail;
55 import org.openhab.core.thing.ThingStatusInfo;
56 import org.openhab.core.thing.binding.BaseThingHandler;
57 import org.openhab.core.types.Command;
58 import org.openhab.core.types.RefreshType;
59 import org.openhab.core.types.TimeSeries;
60 import org.osgi.framework.FrameworkUtil;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
64 import com.google.gson.JsonArray;
65 import com.google.gson.JsonElement;
66 import com.google.gson.JsonObject;
67 import com.google.gson.JsonParser;
68 import com.google.gson.JsonSyntaxException;
71 * The {@link TibberHandler} is responsible for handling queries to/from Tibber API.
73 * @author Stian Kjoglum - Initial contribution
76 public class TibberHandler extends BaseThingHandler {
77 private static final int REQUEST_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(20);
78 private final Logger logger = LoggerFactory.getLogger(TibberHandler.class);
79 private final Properties httpHeader = new Properties();
80 private TibberConfiguration tibberConfig = new TibberConfiguration();
81 private @Nullable SslContextFactory sslContextFactory;
82 private @Nullable TibberWebSocketListener socket;
83 private @Nullable Session session;
84 private @Nullable WebSocketClient client;
85 private @Nullable ScheduledFuture<?> pollingJob;
86 private @Nullable Future<?> sessionFuture;
87 private String rtEnabled = "false";
88 private @Nullable String subscriptionURL;
89 private @Nullable String versionString;
91 public TibberHandler(Thing thing) {
96 public void initialize() {
97 updateStatus(ThingStatus.UNKNOWN);
98 tibberConfig = getConfigAs(TibberConfiguration.class);
100 versionString = FrameworkUtil.getBundle(this.getClass()).getVersion().toString();
101 logger.debug("Binding version: {}", versionString);
103 scheduler.execute(() -> {
104 getTibberParameters();
105 startRefresh(tibberConfig.getRefresh());
110 public void handleCommand(ChannelUID channelUID, Command command) {
111 if (command instanceof RefreshType) {
112 startRefresh(tibberConfig.getRefresh());
114 logger.debug("Tibber API is read-only and does not handle commands");
119 public void channelLinked(ChannelUID channelUID) {
120 if (channelUID.getAsString().contains("live_") && !isConnected() && "true".equals(rtEnabled)) {
123 } catch (IOException e) {
124 logger.debug("Unable to start live data: {}", e.getMessage());
130 public void channelUnlinked(ChannelUID channelUID) {
131 if (channelUID.getAsString().contains("live_") && !liveChannelsLinked() && isConnected()) {
136 public void getTibberParameters() {
137 String response = "";
139 httpHeader.put("cache-control", "no-cache");
140 httpHeader.put("content-type", JSON_CONTENT_TYPE);
141 httpHeader.put(HttpHeader.USER_AGENT.asString(),
142 "openHAB/Tibber " + versionString + " Tibber driver " + TIBBER_DRIVER);
143 httpHeader.put(HttpHeader.AUTHORIZATION.asString(), "Bearer " + tibberConfig.getToken());
145 TibberPriceConsumptionHandler tibberQuery = new TibberPriceConsumptionHandler();
146 InputStream connectionStream = tibberQuery.connectionInputStream(tibberConfig.getHomeid());
147 response = HttpUtil.executeUrl("POST", BASE_URL, httpHeader, connectionStream, null, REQUEST_TIMEOUT);
149 if (!response.contains("error") && !response.contains("<html>")) {
150 updateStatus(ThingStatus.ONLINE);
151 getURLInput(BASE_URL);
153 InputStream inputStream = tibberQuery.getRealtimeInputStream(tibberConfig.getHomeid());
154 String jsonResponse = HttpUtil.executeUrl("POST", BASE_URL, httpHeader, inputStream, null,
157 JsonObject object = (JsonObject) JsonParser.parseString(jsonResponse);
158 JsonObject dObject = object.getAsJsonObject("data");
159 if (dObject != null) {
160 JsonObject viewerObject = dObject.getAsJsonObject("viewer");
161 if (viewerObject != null) {
162 JsonObject homeObject = viewerObject.getAsJsonObject("home");
163 if (homeObject != null) {
164 JsonObject featuresObject = homeObject.getAsJsonObject("features");
165 if (featuresObject != null) {
166 rtEnabled = featuresObject.get("realTimeConsumptionEnabled").toString();
172 if (liveChannelsLinked() && "true".equals(rtEnabled)) {
176 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
177 "Problems connecting/communicating with server: " + response);
179 } catch (IOException | JsonSyntaxException e) {
180 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
184 public void getURLInput(String url) throws IOException {
185 String jsonResponse = "";
186 TibberPriceConsumptionHandler tibberQuery = new TibberPriceConsumptionHandler();
188 InputStream inputStream = tibberQuery.getInputStream(tibberConfig.getHomeid());
189 jsonResponse = HttpUtil.executeUrl("POST", url, httpHeader, inputStream, null, REQUEST_TIMEOUT);
190 logger.debug("API response: {}", jsonResponse);
192 if (!jsonResponse.contains("error") && !jsonResponse.contains("<html>")) {
193 if (getThing().getStatus() == ThingStatus.OFFLINE || getThing().getStatus() == ThingStatus.INITIALIZING) {
194 updateStatus(ThingStatus.ONLINE);
197 JsonObject rootJsonObject = (JsonObject) JsonParser.parseString(jsonResponse);
199 if (jsonResponse.contains("total")) {
201 JsonObject current = rootJsonObject.getAsJsonObject("data").getAsJsonObject("viewer")
202 .getAsJsonObject("home").getAsJsonObject("currentSubscription").getAsJsonObject("priceInfo")
203 .getAsJsonObject("current");
205 updateState(CURRENT_TOTAL, new DecimalType(current.get("total").toString()));
206 String timestamp = current.get("startsAt").toString().substring(1, 20);
207 updateState(CURRENT_STARTSAT, new DateTimeType(timestamp));
208 updateState(CURRENT_LEVEL,
209 new StringType(current.get("level").toString().replaceAll("^\"|\"$", "")));
211 JsonArray tomorrow = rootJsonObject.getAsJsonObject("data").getAsJsonObject("viewer")
212 .getAsJsonObject("home").getAsJsonObject("currentSubscription").getAsJsonObject("priceInfo")
213 .getAsJsonArray("tomorrow");
214 updateState(TOMORROW_PRICES, new StringType(tomorrow.toString()));
215 JsonArray today = rootJsonObject.getAsJsonObject("data").getAsJsonObject("viewer")
216 .getAsJsonObject("home").getAsJsonObject("currentSubscription").getAsJsonObject("priceInfo")
217 .getAsJsonArray("today");
218 updateState(TODAY_PRICES, new StringType(today.toString()));
220 TimeSeries timeSeries = buildTimeSeries(today, tomorrow);
221 sendTimeSeries(CURRENT_TOTAL, timeSeries);
222 } catch (JsonSyntaxException | DateTimeParseException e) {
223 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
224 "Error communicating with Tibber API: " + e.getMessage());
227 if (jsonResponse.contains("daily") && !jsonResponse.contains("\"daily\":{\"nodes\":[]")
228 && !jsonResponse.contains("\"daily\":null")) {
230 JsonObject myObject = (JsonObject) rootJsonObject.getAsJsonObject("data").getAsJsonObject("viewer")
231 .getAsJsonObject("home").getAsJsonObject("daily").getAsJsonArray("nodes").get(0);
233 String timestampDailyFrom = myObject.get("from").toString().substring(1, 20);
234 updateState(DAILY_FROM, new DateTimeType(timestampDailyFrom));
236 String timestampDailyTo = myObject.get("to").toString().substring(1, 20);
237 updateState(DAILY_TO, new DateTimeType(timestampDailyTo));
239 updateChannel(DAILY_COST, myObject.get("cost").toString());
240 updateChannel(DAILY_CONSUMPTION, myObject.get("consumption").toString());
241 } catch (JsonSyntaxException e) {
242 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
243 "Error communicating with Tibber API: " + e.getMessage());
246 if (jsonResponse.contains("hourly") && !jsonResponse.contains("\"hourly\":{\"nodes\":[]")
247 && !jsonResponse.contains("\"hourly\":null")) {
249 JsonObject myObject = (JsonObject) rootJsonObject.getAsJsonObject("data").getAsJsonObject("viewer")
250 .getAsJsonObject("home").getAsJsonObject("hourly").getAsJsonArray("nodes").get(0);
252 String timestampHourlyFrom = myObject.get("from").toString().substring(1, 20);
253 updateState(HOURLY_FROM, new DateTimeType(timestampHourlyFrom));
255 String timestampHourlyTo = myObject.get("to").toString().substring(1, 20);
256 updateState(HOURLY_TO, new DateTimeType(timestampHourlyTo));
258 updateChannel(HOURLY_COST, myObject.get("cost").toString());
259 updateChannel(HOURLY_CONSUMPTION, myObject.get("consumption").toString());
260 } catch (JsonSyntaxException e) {
261 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
262 "Error communicating with Tibber API: " + e.getMessage());
265 } else if (jsonResponse.contains("error")) {
266 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
267 "Error in response from Tibber API: " + jsonResponse);
269 Thread.sleep(300 * 1000);
271 } catch (InterruptedException e) {
272 logger.debug("Tibber OFFLINE, attempting thread sleep: {}", e.getMessage());
275 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
276 "Unexpected response from Tibber: " + jsonResponse);
278 Thread.sleep(300 * 1000);
280 } catch (InterruptedException e) {
281 logger.debug("Tibber OFFLINE, attempting thread sleep: {}", e.getMessage());
287 * Builds the {@link TimeSeries} that represents the future tibber prices.
289 * @param today The prices for today
290 * @param tomorrow The prices for tomorrow.
291 * @return The {@link TimeSeries} with future values.
293 private TimeSeries buildTimeSeries(JsonArray today, JsonArray tomorrow) {
294 final TimeSeries timeSeries = new TimeSeries(TimeSeries.Policy.REPLACE);
295 mapTimeSeriesEntries(today, timeSeries);
296 mapTimeSeriesEntries(tomorrow, timeSeries);
300 private void mapTimeSeriesEntries(JsonArray prices, TimeSeries timeSeries) {
301 for (JsonElement entry : prices) {
302 JsonObject entryObject = entry.getAsJsonObject();
303 final Instant startsAt = ZonedDateTime.parse(entryObject.get("startsAt").getAsString()).toInstant();
304 final DecimalType value = new DecimalType(entryObject.get("total").getAsString());
305 timeSeries.add(startsAt, value);
309 public void startRefresh(int refresh) {
310 if (pollingJob == null) {
311 pollingJob = scheduler.scheduleWithFixedDelay(() -> {
314 } catch (IOException e) {
315 logger.warn("IO Exception: {}", e.getMessage());
317 }, 1, refresh, TimeUnit.MINUTES);
321 public void updateRequest() throws IOException {
322 getURLInput(BASE_URL);
323 if (liveChannelsLinked() && "true".equals(rtEnabled) && !isConnected()) {
328 private boolean liveChannelsLinked() {
329 return getThing().getChannels().stream().map(Channel::getUID)
330 .filter((channelUID -> channelUID.getAsString().contains("live_"))).anyMatch(this::isLinked);
333 private void getSubscriptionUrl() throws IOException {
334 TibberPriceConsumptionHandler tibberQuery = new TibberPriceConsumptionHandler();
335 InputStream wsURL = tibberQuery.getWebsocketUrl();
336 String wsResponse = HttpUtil.executeUrl("POST", BASE_URL, httpHeader, wsURL, null, REQUEST_TIMEOUT);
338 JsonObject wsobject = (JsonObject) JsonParser.parseString(wsResponse);
339 JsonObject dataObject = wsobject.getAsJsonObject("data");
340 if (dataObject != null) {
341 JsonObject viewerObject = dataObject.getAsJsonObject("viewer");
342 if (viewerObject != null) {
343 JsonElement subscriptionElement = viewerObject.get("websocketSubscriptionUrl");
344 if (subscriptionElement != null) {
345 subscriptionURL = subscriptionElement.toString().replaceAll("^\"|\"$", "");
351 public void updateChannel(String channelID, String channelValue) {
352 if (!channelValue.contains("null")) {
353 if (channelID.contains("consumption") || channelID.contains("Consumption")
354 || channelID.contains("accumulatedProduction")) {
355 updateState(channelID, new QuantityType<>(new BigDecimal(channelValue), Units.KILOWATT_HOUR));
356 } else if (channelID.contains("power") || channelID.contains("Power")) {
357 updateState(channelID, new QuantityType<>(new BigDecimal(channelValue), Units.WATT));
358 } else if (channelID.contains("voltage")) {
359 updateState(channelID, new QuantityType<>(new BigDecimal(channelValue), Units.VOLT));
360 } else if (channelID.contains("current")) {
361 updateState(channelID, new QuantityType<>(new BigDecimal(channelValue), Units.AMPERE));
363 updateState(channelID, new DecimalType(channelValue));
368 public void thingStatusChanged(ThingStatusInfo thingStatusInfo) {
369 logger.debug("Thing Status updated to {} for device: {}", thingStatusInfo.getStatus(), getThing().getUID());
370 if (thingStatusInfo.getStatus() != ThingStatus.ONLINE) {
371 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
372 "Unable to communicate with Tibber API");
377 public void dispose() {
378 ScheduledFuture<?> pollingJob = this.pollingJob;
379 if (pollingJob != null) {
380 pollingJob.cancel(true);
381 this.pollingJob = null;
385 WebSocketClient client = this.client;
386 if (client != null) {
388 logger.debug("DISPOSE - Stopping and Terminating Websocket connection");
390 } catch (Exception e) {
391 logger.warn("Websocket Client Stop Exception: {}", e.getMessage());
400 private void startLiveStream() throws IOException {
401 logger.debug("Attempting to open Websocket connection");
402 getSubscriptionUrl();
404 if (subscriptionURL == null || subscriptionURL.isBlank()) {
405 logger.debug("Unexpected subscription result from the server");
406 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
407 "Unexpected subscription result from the server");
409 logger.debug("Connecting Subscription to: {}", subscriptionURL);
415 WebSocketClient client = this.client;
416 if (client == null || !client.isRunning() || !isConnected()) {
417 if (client != null) {
420 } catch (Exception e) {
421 logger.warn("OPEN FRAME - Failed to stop websocket client: {}", e.getMessage());
425 sslContextFactory = new SslContextFactory.Client(true);
426 sslContextFactory.setTrustAll(true);
427 sslContextFactory.setEndpointIdentificationAlgorithm(null);
429 client = new WebSocketClient(new HttpClient(sslContextFactory));
430 client.setMaxIdleTimeout(30 * 1000);
431 this.client = client;
433 TibberWebSocketListener socket = this.socket;
434 if (socket == null) {
435 logger.debug("New socket being created");
436 socket = new TibberWebSocketListener();
437 this.socket = socket;
440 ClientUpgradeRequest newRequest = new ClientUpgradeRequest();
441 newRequest.setHeader(HttpHeader.USER_AGENT.asString(),
442 "openHAB/Tibber " + versionString + " Tibber driver " + TIBBER_DRIVER);
443 newRequest.setHeader(HttpHeader.AUTHORIZATION.asString(), "Bearer " + tibberConfig.getToken());
444 newRequest.setSubProtocols("graphql-transport-ws");
447 logger.debug("Starting Websocket connection");
449 } catch (Exception e) {
450 logger.warn("Websocket Start Exception: {}", e.getMessage());
453 logger.debug("Connecting Websocket connection");
454 sessionFuture = client.connect(socket, new URI(subscriptionURL), newRequest);
456 Thread.sleep(10 * 1000);
457 } catch (InterruptedException e) {
459 if (!isConnected()) {
460 logger.warn("Unable to establish websocket session - Reattempting connection on next refresh");
462 logger.debug("Websocket session established");
464 } catch (IOException e) {
465 logger.warn("Websocket Connect Exception: {}", e.getMessage());
466 } catch (URISyntaxException e) {
467 logger.warn("Websocket URI Exception: {}", e.getMessage());
470 logger.warn("Open: Websocket client already running");
474 public void close() {
475 Session session = this.session;
476 if (session != null) {
477 String disconnect = "{\"type\":\"connection_terminate\",\"payload\":null}";
479 TibberWebSocketListener socket = this.socket;
480 if (socket != null) {
481 logger.debug("Sending websocket disconnect message");
482 socket.sendMessage(disconnect);
484 logger.warn("Socket unable to send disconnect message: Socket is null");
486 } catch (IOException e) {
487 logger.warn("Websocket Close Exception: {}", e.getMessage());
491 } catch (Exception e) {
492 logger.warn("Unable to disconnect session");
497 Future<?> sessionFuture = this.sessionFuture;
498 if (sessionFuture != null && !sessionFuture.isDone()) {
499 sessionFuture.cancel(true);
501 WebSocketClient client = this.client;
502 if (client != null) {
505 } catch (Exception e) {
506 logger.warn("CLOSE FRAME - Failed to stop websocket client: {}", e.getMessage());
512 public boolean isConnected() {
513 Session session = this.session;
514 return session != null && session.isOpen();
518 public class TibberWebSocketListener {
521 public void onConnect(Session wssession) {
522 TibberHandler.this.session = wssession;
523 TibberWebSocketListener socket = TibberHandler.this.socket;
524 String connection = "{\"type\":\"connection_init\", \"payload\":{\"token\":\"" + tibberConfig.getToken()
527 if (socket != null) {
528 logger.debug("Sending websocket connect message");
529 socket.sendMessage(connection);
531 logger.debug("Socket unable to send connect message: Socket is null");
533 } catch (IOException e) {
534 logger.warn("Send Message Exception: {}", e.getMessage());
539 public void onClose(int statusCode, String reason) {
540 logger.debug("Closing a WebSocket due to {}", reason);
541 WebSocketClient client = TibberHandler.this.client;
542 if (client != null && client.isRunning()) {
544 logger.debug("ONCLOSE - Stopping and Terminating Websocket connection");
546 } catch (Exception e) {
547 logger.warn("Websocket Client Stop Exception: {}", e.getMessage());
553 public void onWebSocketError(Throwable e) {
554 String message = e.getMessage();
555 logger.debug("Error during websocket communication: {}", message);
560 public void onMessage(String message) {
561 if (message.contains("connection_ack")) {
562 logger.debug("Connected to Server");
564 } else if (message.contains("error") || message.contains("terminate")) {
565 logger.debug("Error/terminate received from server: {}", message);
567 } else if (message.contains("liveMeasurement")) {
568 JsonObject object = (JsonObject) JsonParser.parseString(message);
569 JsonObject myObject = object.getAsJsonObject("payload").getAsJsonObject("data")
570 .getAsJsonObject("liveMeasurement");
571 if (myObject.has("timestamp")) {
572 String liveTimestamp = myObject.get("timestamp").toString().substring(1, 20);
573 updateState(LIVE_TIMESTAMP, new DateTimeType(liveTimestamp));
575 if (myObject.has("power")) {
576 updateChannel(LIVE_POWER, myObject.get("power").toString());
578 if (myObject.has("lastMeterConsumption")) {
579 updateChannel(LIVE_LASTMETERCONSUMPTION, myObject.get("lastMeterConsumption").toString());
581 if (myObject.has("lastMeterProduction")) {
582 updateChannel(LIVE_LASTMETERPRODUCTION, myObject.get("lastMeterProduction").toString());
584 if (myObject.has("accumulatedConsumption")) {
585 updateChannel(LIVE_ACCUMULATEDCONSUMPTION, myObject.get("accumulatedConsumption").toString());
587 if (myObject.has("accumulatedConsumptionLastHour")) {
588 updateChannel(LIVE_ACCUMULATEDCONSUMPTION_THIS_HOUR,
589 myObject.get("accumulatedConsumptionLastHour").toString());
591 if (myObject.has("accumulatedCost")) {
592 updateChannel(LIVE_ACCUMULATEDCOST, myObject.get("accumulatedCost").toString());
594 if (myObject.has("accumulatedReward")) {
595 updateChannel(LIVE_ACCUMULATEREWARD, myObject.get("accumulatedReward").toString());
597 if (myObject.has("currency")) {
598 updateState(LIVE_CURRENCY, new StringType(myObject.get("currency").toString()));
600 if (myObject.has("minPower")) {
601 updateChannel(LIVE_MINPOWER, myObject.get("minPower").toString());
603 if (myObject.has("averagePower")) {
604 updateChannel(LIVE_AVERAGEPOWER, myObject.get("averagePower").toString());
606 if (myObject.has("maxPower")) {
607 updateChannel(LIVE_MAXPOWER, myObject.get("maxPower").toString());
609 if (myObject.has("voltagePhase1")) {
610 updateChannel(LIVE_VOLTAGE1, myObject.get("voltagePhase1").toString());
612 if (myObject.has("voltagePhase2")) {
613 updateChannel(LIVE_VOLTAGE2, myObject.get("voltagePhase2").toString());
615 if (myObject.has("voltagePhase3")) {
616 updateChannel(LIVE_VOLTAGE3, myObject.get("voltagePhase3").toString());
618 if (myObject.has("currentL1")) {
619 updateChannel(LIVE_CURRENT1, myObject.get("currentL1").toString());
621 if (myObject.has("currentL2")) {
622 updateChannel(LIVE_CURRENT2, myObject.get("currentL2").toString());
624 if (myObject.has("currentL3")) {
625 updateChannel(LIVE_CURRENT3, myObject.get("currentL3").toString());
627 if (myObject.has("powerProduction")) {
628 updateChannel(LIVE_POWERPRODUCTION, myObject.get("powerProduction").toString());
630 if (myObject.has("accumulatedProduction")) {
631 updateChannel(LIVE_ACCUMULATEDPRODUCTION, myObject.get("accumulatedProduction").toString());
633 if (myObject.has("accumulatedProductionLastHour")) {
634 updateChannel(LIVE_ACCUMULATEDPRODUCTION_THIS_HOUR,
635 myObject.get("accumulatedProductionLastHour").toString());
637 if (myObject.has("minPowerProduction")) {
638 updateChannel(LIVE_MINPOWERPRODUCTION, myObject.get("minPowerProduction").toString());
640 if (myObject.has("maxPowerProduction")) {
641 updateChannel(LIVE_MAXPOWERPRODUCTION, myObject.get("maxPowerProduction").toString());
644 logger.debug("Unknown live response from Tibber");
648 private void sendMessage(String message) throws IOException {
649 logger.debug("Send message: {}", message);
650 Session session = TibberHandler.this.session;
651 if (session != null) {
652 session.getRemote().sendString(message);
656 public void startSubscription() {
657 String query = "{\"id\":\"1\",\"type\":\"subscribe\",\"payload\":{\"variables\":{},\"extensions\":{},\"operationName\":null,\"query\":\"subscription {\\n liveMeasurement(homeId:\\\""
658 + tibberConfig.getHomeid()
659 + "\\\") {\\n timestamp\\n power\\n lastMeterConsumption\\n lastMeterProduction\\n accumulatedConsumption\\n accumulatedConsumptionLastHour\\n accumulatedCost\\n accumulatedReward\\n currency\\n minPower\\n averagePower\\n maxPower\\n"
660 + "voltagePhase1\\n voltagePhase2\\n voltagePhase3\\n currentL1\\n currentL2\\n currentL3\\n powerProduction\\n accumulatedProduction\\n accumulatedProductionLastHour\\n minPowerProduction\\n maxPowerProduction\\n }\\n }\\n\"}}";
662 TibberWebSocketListener socket = TibberHandler.this.socket;
663 if (socket != null) {
664 socket.sendMessage(query);
666 logger.debug("Socket unable to send subscription message: Socket is null");
668 } catch (IOException e) {
669 logger.warn("Send Message Exception: {}", e.getMessage());