2 * Copyright (c) 2010-2021 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
47 * @author Thomas Kordelle - Added inverter power, battery state of charge and PV solar yield
49 public abstract class FroniusBaseThingHandler extends BaseThingHandler {
51 private static final int API_TIMEOUT = 5000;
52 private final Logger logger = LoggerFactory.getLogger(FroniusBaseThingHandler.class);
53 private final String serviceDescription;
54 private FroniusBridgeHandler bridgeHandler;
55 private final Gson gson;
57 public FroniusBaseThingHandler(Thing thing) {
60 serviceDescription = getDescription();
64 public void handleCommand(ChannelUID channelUID, Command command) {
65 if (command instanceof RefreshType) {
66 updateChannel(channelUID.getId());
71 public void initialize() {
72 if (getFroniusBridgeHandler() == null) {
73 logger.debug("Initializing {} Service is only supported within a bridge", serviceDescription);
74 updateStatus(ThingStatus.OFFLINE);
77 logger.debug("Initializing {} Service", serviceDescription);
78 getFroniusBridgeHandler().registerService(this);
81 private synchronized FroniusBridgeHandler getFroniusBridgeHandler() {
82 if (this.bridgeHandler == null) {
83 Bridge bridge = getBridge();
87 ThingHandler handler = bridge.getHandler();
88 if (handler instanceof FroniusBridgeHandler) {
89 this.bridgeHandler = (FroniusBridgeHandler) handler;
94 return this.bridgeHandler;
100 protected void updateChannels() {
101 for (Channel channel : getThing().getChannels()) {
102 updateChannel(channel.getUID().getId());
107 * Update the channel from the last data
109 * @param channelId the id identifying the channel to be updated
111 protected void updateChannel(String channelId) {
112 if (!isLinked(channelId)) {
116 Object value = getValue(channelId);
118 logger.debug("Value retrieved for channel '{}' was null. Can't update.", channelId);
123 if (value instanceof BigDecimal) {
124 state = new DecimalType((BigDecimal) value);
125 } else if (value instanceof Integer) {
126 state = new DecimalType(BigDecimal.valueOf(((Integer) value).longValue()));
127 } else if (value instanceof Double) {
128 state = new DecimalType((double) value);
129 } else if (value instanceof ValueUnit) {
130 state = new DecimalType(((ValueUnit) value).getValue());
131 } else if (value instanceof String) {
132 state = new StringType((String) value);
133 } else if (value instanceof QuantityType) {
134 state = (QuantityType) value;
136 logger.warn("Update channel {}: Unsupported value type {}", channelId, value.getClass().getSimpleName());
138 logger.debug("Update channel {} with state {} ({})", channelId, (state == null) ? "null" : state.toString(),
139 value.getClass().getSimpleName());
141 // Update the channel
143 updateState(channelId, state);
148 * return an internal description for logging
150 * @return the description of the thing
152 protected abstract String getDescription();
155 * get the "new" associated value for a channelId
157 * @param channelId the id identifying the channel
158 * @return the "new" associated value
160 protected abstract Object getValue(String channelId);
163 * do whatever a thing must do to update the values
164 * this function is called from the bridge in a given interval
166 * @param bridgeConfiguration the connected bridge configuration
168 public abstract void refresh(FroniusBridgeConfiguration bridgeConfiguration);
172 * @param type response class type
173 * @param url to request
174 * @return the object representation of the json response
176 protected <T extends BaseFroniusResponse> T collectDataFormUrl(Class<T> type, String url) {
178 boolean resultOk = false;
179 String errorMsg = null;
182 logger.debug("URL = {}", url);
183 String response = HttpUtil.executeUrl("GET", url, API_TIMEOUT);
185 if (response != null) {
186 logger.debug("aqiResponse = {}", response);
187 result = gson.fromJson(response, type);
190 if (result == null) {
191 errorMsg = "no data returned";
193 if (result.getHead().getStatus().getCode() == 0) {
196 errorMsg = result.getHead().getStatus().getReason();
200 logger.debug("Error in fronius response: {}", errorMsg);
202 } catch (JsonSyntaxException | NumberFormatException e) {
203 errorMsg = "Invalid JSON data received";
204 logger.debug("Error running fronius request: {}", e.getMessage());
205 } catch (IOException | IllegalStateException e) {
206 errorMsg = e.getMessage();
207 logger.debug("Error running fronius request: {}", errorMsg);
210 // Update the thing status
212 updateStatus(ThingStatus.ONLINE);
214 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, errorMsg);
216 return resultOk ? result : null;