2 * Copyright (c) 2010-2020 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.fronius.internal.handler;
15 import java.io.IOException;
16 import java.math.BigDecimal;
18 import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration;
19 import org.openhab.binding.fronius.internal.api.BaseFroniusResponse;
20 import org.openhab.binding.fronius.internal.api.ValueUnit;
21 import org.openhab.core.io.net.http.HttpUtil;
22 import org.openhab.core.library.types.DecimalType;
23 import org.openhab.core.library.types.QuantityType;
24 import org.openhab.core.library.types.StringType;
25 import org.openhab.core.thing.Bridge;
26 import org.openhab.core.thing.Channel;
27 import org.openhab.core.thing.ChannelUID;
28 import org.openhab.core.thing.Thing;
29 import org.openhab.core.thing.ThingStatus;
30 import org.openhab.core.thing.ThingStatusDetail;
31 import org.openhab.core.thing.binding.BaseThingHandler;
32 import org.openhab.core.thing.binding.ThingHandler;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.RefreshType;
35 import org.openhab.core.types.State;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 import com.google.gson.Gson;
40 import com.google.gson.JsonSyntaxException;
43 * Basic Handler class for all Fronius services.
45 * @author Gerrit Beine - Initial contribution
46 * @author Thomas Rokohl - Refactoring to merge the concepts
48 public abstract class FroniusBaseThingHandler extends BaseThingHandler {
50 private static final int API_TIMEOUT = 5000;
51 private final Logger logger = LoggerFactory.getLogger(FroniusBaseThingHandler.class);
52 private final String serviceDescription;
53 private FroniusBridgeHandler bridgeHandler;
54 private final Gson gson;
56 public FroniusBaseThingHandler(Thing thing) {
59 serviceDescription = getDescription();
63 public void handleCommand(ChannelUID channelUID, Command command) {
64 if (command instanceof RefreshType) {
65 updateChannel(channelUID.getId());
70 public void initialize() {
71 if (getFroniusBridgeHandler() == null) {
72 logger.debug("Initializing {} Service is only supported within a bridge", serviceDescription);
73 updateStatus(ThingStatus.OFFLINE);
76 logger.debug("Initializing {} Service", serviceDescription);
77 getFroniusBridgeHandler().registerService(this);
80 private synchronized FroniusBridgeHandler getFroniusBridgeHandler() {
81 if (this.bridgeHandler == null) {
82 Bridge bridge = getBridge();
86 ThingHandler handler = bridge.getHandler();
87 if (handler instanceof FroniusBridgeHandler) {
88 this.bridgeHandler = (FroniusBridgeHandler) handler;
93 return this.bridgeHandler;
99 protected void updateChannels() {
100 for (Channel channel : getThing().getChannels()) {
101 updateChannel(channel.getUID().getId());
106 * Update the channel from the last data
108 * @param channelId the id identifying the channel to be updated
110 protected void updateChannel(String channelId) {
111 if (!isLinked(channelId)) {
115 Object value = getValue(channelId);
117 logger.debug("Value retrieved for channel '{}' was null. Can't update.", channelId);
122 if (value instanceof BigDecimal) {
123 state = new DecimalType((BigDecimal) value);
124 } else if (value instanceof Integer) {
125 state = new DecimalType(BigDecimal.valueOf(((Integer) value).longValue()));
126 } else if (value instanceof Double) {
127 state = new DecimalType((double) value);
128 } else if (value instanceof ValueUnit) {
129 state = new DecimalType(((ValueUnit) value).getValue());
130 } else if (value instanceof String) {
131 state = new StringType((String) value);
132 } else if (value instanceof QuantityType) {
133 state = (QuantityType) value;
135 logger.warn("Update channel {}: Unsupported value type {}", channelId, value.getClass().getSimpleName());
137 logger.debug("Update channel {} with state {} ({})", channelId, (state == null) ? "null" : state.toString(),
138 value.getClass().getSimpleName());
140 // Update the channel
142 updateState(channelId, state);
147 * return an internal description for logging
149 * @return the description of the thing
151 protected abstract String getDescription();
154 * get the "new" associated value for a channelId
156 * @param channelId the id identifying the channel
157 * @return the "new" associated value
159 protected abstract Object getValue(String channelId);
162 * do whatever a thing must do to update the values
163 * this function is called from the bridge in a given interval
165 * @param bridgeConfiguration the connected bridge configuration
167 public abstract void refresh(FroniusBridgeConfiguration bridgeConfiguration);
171 * @param type response class type
172 * @param url to request
173 * @return the object representation of the json response
175 protected <T extends BaseFroniusResponse> T collectDataFormUrl(Class<T> type, String url) {
177 boolean resultOk = false;
178 String errorMsg = null;
181 logger.debug("URL = {}", url);
182 String response = HttpUtil.executeUrl("GET", url, API_TIMEOUT);
184 if (response != null) {
185 logger.debug("aqiResponse = {}", response);
186 result = gson.fromJson(response, type);
189 if (result == null) {
190 errorMsg = "no data returned";
192 if (result.getHead().getStatus().getCode() == 0) {
195 errorMsg = result.getHead().getStatus().getReason();
199 logger.debug("Error in fronius response: {}", errorMsg);
201 } catch (JsonSyntaxException e) {
202 errorMsg = "Invalid JSON data received";
203 logger.debug("Error running fronius request: {}", errorMsg);
204 } catch (IOException | IllegalStateException e) {
205 errorMsg = e.getMessage();
206 logger.debug("Error running fronius request: {}", errorMsg);
209 // Update the thing status
211 updateStatus(ThingStatus.ONLINE);
213 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, errorMsg);
215 return resultOk ? result : null;