2 * Copyright (c) 2010-2022 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.util.Optional;
17 import javax.measure.Unit;
19 import org.openhab.binding.fronius.internal.FroniusBaseDeviceConfiguration;
20 import org.openhab.binding.fronius.internal.FroniusBindingConstants;
21 import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration;
22 import org.openhab.binding.fronius.internal.FroniusCommunicationException;
23 import org.openhab.binding.fronius.internal.api.InverterRealtimeBodyData;
24 import org.openhab.binding.fronius.internal.api.InverterRealtimeResponse;
25 import org.openhab.binding.fronius.internal.api.PowerFlowRealtimeInverter;
26 import org.openhab.binding.fronius.internal.api.PowerFlowRealtimeResponse;
27 import org.openhab.binding.fronius.internal.api.PowerFlowRealtimeSite;
28 import org.openhab.binding.fronius.internal.api.ValueUnit;
29 import org.openhab.core.library.types.DecimalType;
30 import org.openhab.core.library.types.QuantityType;
31 import org.openhab.core.library.unit.Units;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.types.State;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * The {@link FroniusSymoInverterHandler} is responsible for updating the data, which are
39 * sent to one of the channels.
41 * @author Thomas Rokohl - Initial contribution
42 * @author Peter Schraffl - Added device status and error status channels
43 * @author Thomas Kordelle - Added inverter power, battery state of charge and PV solar yield
44 * @author Jimmy Tanagra - Add powerflow autonomy, self consumption channels
46 public class FroniusSymoInverterHandler extends FroniusBaseThingHandler {
48 private final Logger logger = LoggerFactory.getLogger(FroniusSymoInverterHandler.class);
49 private InverterRealtimeResponse inverterRealtimeResponse;
50 private PowerFlowRealtimeResponse powerFlowResponse;
51 private FroniusBaseDeviceConfiguration config;
53 public FroniusSymoInverterHandler(Thing thing) {
58 protected String getDescription() {
59 return "Fronius Symo Inverter";
63 protected void handleRefresh(FroniusBridgeConfiguration bridgeConfiguration) throws FroniusCommunicationException {
64 updateData(bridgeConfiguration, config);
69 public void initialize() {
70 config = getConfigAs(FroniusBaseDeviceConfiguration.class);
75 * Update the channel from the last data retrieved
77 * @param channelId the id identifying the channel to be updated
78 * @return the last retrieved data
81 protected State getValue(String channelId) {
82 final String[] fields = channelId.split("#");
83 if (fields.length < 1) {
86 final String fieldName = fields[0];
88 if (inverterRealtimeResponse != null) {
89 InverterRealtimeBodyData inverterData = inverterRealtimeResponse.getBody().getData();
91 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_PAC:
92 return getQuantityOrZero(inverterData.getPac(), Units.WATT);
93 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_FAC:
94 return getQuantityOrZero(inverterData.getFac(), Units.HERTZ);
95 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_IAC:
96 return getQuantityOrZero(inverterData.getIac(), Units.AMPERE);
97 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_IDC:
98 return getQuantityOrZero(inverterData.getIdc(), Units.AMPERE);
99 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_UAC:
100 return getQuantityOrZero(inverterData.getUac(), Units.VOLT);
101 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_UDC:
102 return getQuantityOrZero(inverterData.getUdc(), Units.VOLT);
103 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DAY_ENERGY:
104 // Convert the unit to kWh for backwards compatibility with non-quantity type
105 return getQuantityOrZero(inverterData.getDayEnergy(), Units.KILOWATT_HOUR).toUnit("kWh");
106 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_TOTAL:
107 // Convert the unit to MWh for backwards compatibility with non-quantity type
108 return getQuantityOrZero(inverterData.getTotalEnergy(), Units.MEGAWATT_HOUR).toUnit("MWh");
109 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_YEAR:
110 // Convert the unit to MWh for backwards compatibility with non-quantity type
111 return getQuantityOrZero(inverterData.getYearEnergy(), Units.MEGAWATT_HOUR).toUnit("MWh");
112 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DEVICE_STATUS_ERROR_CODE:
113 return new DecimalType(inverterData.getDeviceStatus().getErrorCode());
114 case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DEVICE_STATUS_STATUS_CODE:
115 return new DecimalType(inverterData.getDeviceStatus().getStatusCode());
121 if (powerFlowResponse != null) {
122 PowerFlowRealtimeSite site = powerFlowResponse.getBody().getData().getSite();
124 case FroniusBindingConstants.POWER_FLOW_P_GRID:
125 return new QuantityType<>(site.getPgrid(), Units.WATT);
126 case FroniusBindingConstants.POWER_FLOW_P_LOAD:
127 return new QuantityType<>(site.getPload(), Units.WATT);
128 case FroniusBindingConstants.POWER_FLOW_P_AKKU:
129 return new QuantityType<>(site.getPakku(), Units.WATT);
130 case FroniusBindingConstants.POWER_FLOW_P_PV:
131 return new QuantityType<>(site.getPpv(), Units.WATT);
132 case FroniusBindingConstants.POWER_FLOW_AUTONOMY:
133 return new QuantityType<>(site.getRelAutonomy(), Units.PERCENT);
134 case FroniusBindingConstants.POWER_FLOW_SELF_CONSUMPTION:
135 return new QuantityType<>(site.getRelSelfConsumption(), Units.PERCENT);
136 case FroniusBindingConstants.POWER_FLOW_INVERTER_1_POWER:
137 return new QuantityType<>(getInverter("1").getP(), Units.WATT);
138 case FroniusBindingConstants.POWER_FLOW_INVERTER_1_SOC:
139 return new QuantityType<>(getInverter("1").getSoc(), Units.PERCENT);
149 * get flow data for a specific inverter.
151 * @param number The inverter object of the given index
152 * @return an PowerFlowRealtimeInverter object.
154 private PowerFlowRealtimeInverter getInverter(final String number) {
155 return powerFlowResponse.getBody().getData().getInverters().get(number);
159 * Return the value as QuantityType with the unit extracted from ValueUnit
160 * or a zero QuantityType with the given unit argument when value is null
162 * @param value The ValueUnit data
163 * @param unit The default unit to use when value is null
164 * @return a QuantityType from the given value
166 private QuantityType<?> getQuantityOrZero(ValueUnit value, Unit unit) {
167 return Optional.ofNullable(value).map(val -> val.asQuantityType().toUnit(unit))
168 .orElse(new QuantityType<>(0, unit));
174 private void updateData(FroniusBridgeConfiguration bridgeConfiguration, FroniusBaseDeviceConfiguration config)
175 throws FroniusCommunicationException {
176 inverterRealtimeResponse = getRealtimeData(bridgeConfiguration.hostname, config.deviceId);
177 powerFlowResponse = getPowerFlowRealtime(bridgeConfiguration.hostname);
181 * Make the PowerFlowRealtimeDataRequest
183 * @param ip address of the device
184 * @return {PowerFlowRealtimeResponse} the object representation of the json response
186 private PowerFlowRealtimeResponse getPowerFlowRealtime(String ip) throws FroniusCommunicationException {
187 String location = FroniusBindingConstants.getPowerFlowDataUrl(ip);
188 return collectDataFromUrl(PowerFlowRealtimeResponse.class, location);
192 * Make the InverterRealtimeDataRequest
194 * @param ip address of the device
195 * @param deviceId of the device
196 * @return {InverterRealtimeResponse} the object representation of the json response
198 private InverterRealtimeResponse getRealtimeData(String ip, int deviceId) throws FroniusCommunicationException {
199 String location = FroniusBindingConstants.getInverterDataUrl(ip, deviceId);
200 return collectDataFromUrl(InverterRealtimeResponse.class, location);