]> git.basschouten.com Git - openhab-addons.git/blob
803009756c93145fc13370a9c5ec4f7cfafc976f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.fronius.internal.handler;
14
15 import java.util.Optional;
16
17 import javax.measure.Unit;
18
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.openhab.binding.fronius.internal.FroniusBaseDeviceConfiguration;
21 import org.openhab.binding.fronius.internal.FroniusBindingConstants;
22 import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration;
23 import org.openhab.binding.fronius.internal.api.FroniusCommunicationException;
24 import org.openhab.binding.fronius.internal.api.dto.ValueUnit;
25 import org.openhab.binding.fronius.internal.api.dto.inverter.InverterDeviceStatus;
26 import org.openhab.binding.fronius.internal.api.dto.inverter.InverterRealtimeBody;
27 import org.openhab.binding.fronius.internal.api.dto.inverter.InverterRealtimeBodyData;
28 import org.openhab.binding.fronius.internal.api.dto.inverter.InverterRealtimeResponse;
29 import org.openhab.binding.fronius.internal.api.dto.powerflow.PowerFlowRealtimeBody;
30 import org.openhab.binding.fronius.internal.api.dto.powerflow.PowerFlowRealtimeBodyData;
31 import org.openhab.binding.fronius.internal.api.dto.powerflow.PowerFlowRealtimeInverter;
32 import org.openhab.binding.fronius.internal.api.dto.powerflow.PowerFlowRealtimeResponse;
33 import org.openhab.binding.fronius.internal.api.dto.powerflow.PowerFlowRealtimeSite;
34 import org.openhab.core.library.types.DecimalType;
35 import org.openhab.core.library.types.QuantityType;
36 import org.openhab.core.library.unit.Units;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.types.State;
39
40 /**
41  * The {@link FroniusSymoInverterHandler} is responsible for updating the data, which are
42  * sent to one of the channels.
43  *
44  * @author Thomas Rokohl - Initial contribution
45  * @author Peter Schraffl - Added device status and error status channels
46  * @author Thomas Kordelle - Added inverter power, battery state of charge and PV solar yield
47  * @author Jimmy Tanagra - Add powerflow autonomy, self consumption channels
48  */
49 public class FroniusSymoInverterHandler extends FroniusBaseThingHandler {
50
51     private @Nullable InverterRealtimeResponse inverterRealtimeResponse;
52     private @Nullable PowerFlowRealtimeResponse powerFlowResponse;
53     private FroniusBaseDeviceConfiguration config;
54
55     public FroniusSymoInverterHandler(Thing thing) {
56         super(thing);
57     }
58
59     @Override
60     protected String getDescription() {
61         return "Fronius Symo Inverter";
62     }
63
64     @Override
65     protected void handleRefresh(FroniusBridgeConfiguration bridgeConfiguration) throws FroniusCommunicationException {
66         updateData(bridgeConfiguration, config);
67         updateChannels();
68     }
69
70     @Override
71     public void initialize() {
72         config = getConfigAs(FroniusBaseDeviceConfiguration.class);
73         super.initialize();
74     }
75
76     /**
77      * Update the channel from the last data retrieved
78      *
79      * @param channelId the id identifying the channel to be updated
80      * @return the last retrieved data
81      */
82     @Override
83     protected State getValue(String channelId) {
84         final String[] fields = channelId.split("#");
85         if (fields.length < 1) {
86             return null;
87         }
88         final String fieldName = fields[0];
89
90         InverterRealtimeBodyData inverterData = getInverterData();
91         if (inverterData == null) {
92             return null;
93         }
94
95         switch (fieldName) {
96             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_PAC:
97                 return getQuantityOrZero(inverterData.getPac(), Units.WATT);
98             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_FAC:
99                 return getQuantityOrZero(inverterData.getFac(), Units.HERTZ);
100             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_IAC:
101                 return getQuantityOrZero(inverterData.getIac(), Units.AMPERE);
102             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_IDC:
103                 return getQuantityOrZero(inverterData.getIdc(), Units.AMPERE);
104             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_IDC2:
105                 return getQuantityOrZero(inverterData.getIdc2(), Units.AMPERE);
106             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_IDC3:
107                 return getQuantityOrZero(inverterData.getIdc3(), Units.AMPERE);
108             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_UAC:
109                 return getQuantityOrZero(inverterData.getUac(), Units.VOLT);
110             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_UDC:
111                 return getQuantityOrZero(inverterData.getUdc(), Units.VOLT);
112             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_UDC2:
113                 return getQuantityOrZero(inverterData.getUdc2(), Units.VOLT);
114             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_UDC3:
115                 return getQuantityOrZero(inverterData.getUdc3(), Units.VOLT);
116             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_PDC:
117                 return calculatePower(inverterData.getUdc(), inverterData.getIdc());
118             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_PDC2:
119                 return calculatePower(inverterData.getUdc2(), inverterData.getIdc2());
120             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_PDC3:
121                 return calculatePower(inverterData.getUdc3(), inverterData.getIdc3());
122             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DAY_ENERGY:
123                 // Convert the unit to kWh for backwards compatibility with non-quantity type
124                 return getQuantityOrZero(inverterData.getDayEnergy(), Units.KILOWATT_HOUR).toUnit("kWh");
125             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_TOTAL:
126                 // Convert the unit to MWh for backwards compatibility with non-quantity type
127                 return getQuantityOrZero(inverterData.getTotalEnergy(), Units.MEGAWATT_HOUR).toUnit("MWh");
128             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_YEAR:
129                 // Convert the unit to MWh for backwards compatibility with non-quantity type
130                 return getQuantityOrZero(inverterData.getYearEnergy(), Units.MEGAWATT_HOUR).toUnit("MWh");
131             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DEVICE_STATUS_ERROR_CODE:
132                 InverterDeviceStatus deviceStatus = inverterData.getDeviceStatus();
133                 if (deviceStatus == null) {
134                     return null;
135                 }
136                 return new DecimalType(deviceStatus.getErrorCode());
137             case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DEVICE_STATUS_STATUS_CODE:
138                 deviceStatus = inverterData.getDeviceStatus();
139                 if (deviceStatus == null) {
140                     return null;
141                 }
142                 return new DecimalType(deviceStatus.getStatusCode());
143             default:
144                 break;
145         }
146
147         PowerFlowRealtimeBodyData powerFlowData = getPowerFlowRealtimeData();
148         if (powerFlowData == null) {
149             return null;
150         }
151         PowerFlowRealtimeSite site = powerFlowData.getSite();
152         if (site == null) {
153             return null;
154         }
155
156         return switch (fieldName) {
157             case FroniusBindingConstants.POWER_FLOW_P_GRID -> new QuantityType<>(site.getPgrid(), Units.WATT);
158             case FroniusBindingConstants.POWER_FLOW_P_LOAD -> new QuantityType<>(site.getPload(), Units.WATT);
159             case FroniusBindingConstants.POWER_FLOW_P_AKKU -> new QuantityType<>(site.getPakku(), Units.WATT);
160             case FroniusBindingConstants.POWER_FLOW_P_PV -> new QuantityType<>(site.getPpv(), Units.WATT);
161             case FroniusBindingConstants.POWER_FLOW_AUTONOMY ->
162                 new QuantityType<>(site.getRelAutonomy(), Units.PERCENT);
163             case FroniusBindingConstants.POWER_FLOW_SELF_CONSUMPTION ->
164                 new QuantityType<>(site.getRelSelfConsumption(), Units.PERCENT);
165             case FroniusBindingConstants.POWER_FLOW_INVERTER_POWER -> {
166                 PowerFlowRealtimeInverter inverter = getInverter(config.deviceId);
167                 if (inverter == null) {
168                     yield null;
169                 }
170                 yield new QuantityType<>(inverter.getP(), Units.WATT);
171             }
172             case FroniusBindingConstants.POWER_FLOW_INVERTER_SOC -> {
173                 PowerFlowRealtimeInverter inverter = getInverter(config.deviceId);
174                 if (inverter == null) {
175                     yield null;
176                 }
177                 yield new QuantityType<>(inverter.getSoc(), Units.PERCENT);
178             }
179             // Kept for backwards compatibility
180             case FroniusBindingConstants.POWER_FLOW_INVERTER_1_POWER -> {
181                 PowerFlowRealtimeInverter inverter = getInverter(1);
182                 if (inverter == null) {
183                     yield null;
184                 }
185                 yield new QuantityType<>(inverter.getP(), Units.WATT);
186             }
187             case FroniusBindingConstants.POWER_FLOW_INVERTER_1_SOC -> {
188                 PowerFlowRealtimeInverter inverter = getInverter(1);
189                 if (inverter == null) {
190                     yield null;
191                 }
192                 yield new QuantityType<>(inverter.getSoc(), Units.PERCENT);
193             }
194
195             default -> null;
196         };
197     }
198
199     private @Nullable InverterRealtimeBodyData getInverterData() {
200         InverterRealtimeResponse localInverterRealtimeResponse = inverterRealtimeResponse;
201         if (localInverterRealtimeResponse == null) {
202             return null;
203         }
204         InverterRealtimeBody inverterBody = localInverterRealtimeResponse.getBody();
205         return (inverterBody != null) ? inverterBody.getData() : null;
206     }
207
208     private @Nullable PowerFlowRealtimeBodyData getPowerFlowRealtimeData() {
209         PowerFlowRealtimeResponse localPowerFlowResponse = powerFlowResponse;
210         if (localPowerFlowResponse == null) {
211             return null;
212         }
213         PowerFlowRealtimeBody powerFlowBody = localPowerFlowResponse.getBody();
214         return (powerFlowBody != null) ? powerFlowBody.getData() : null;
215     }
216
217     /**
218      * get flow data for a specific inverter.
219      *
220      * @param number The inverter object of the given index
221      * @return a PowerFlowRealtimeInverter object.
222      */
223     private @Nullable PowerFlowRealtimeInverter getInverter(final int number) {
224         PowerFlowRealtimeBodyData powerFlowData = getPowerFlowRealtimeData();
225         if (powerFlowData == null) {
226             return null;
227         }
228         return powerFlowData.getInverters().get(Integer.toString(number));
229     }
230
231     /**
232      * Return the value as QuantityType with the unit extracted from ValueUnit
233      * or a zero QuantityType with the given unit argument when value is null
234      * 
235      * @param value The ValueUnit data
236      * @param unit The default unit to use when value is null
237      * @return a QuantityType from the given value
238      */
239     private QuantityType<?> getQuantityOrZero(ValueUnit value, Unit unit) {
240         return Optional.ofNullable(value).map(val -> val.asQuantityType().toUnit(unit))
241                 .orElse(new QuantityType<>(0, unit));
242     }
243
244     /**
245      * Get new data
246      */
247     private void updateData(FroniusBridgeConfiguration bridgeConfiguration, FroniusBaseDeviceConfiguration config)
248             throws FroniusCommunicationException {
249         inverterRealtimeResponse = getRealtimeData(bridgeConfiguration.hostname, config.deviceId);
250         powerFlowResponse = getPowerFlowRealtime(bridgeConfiguration.hostname);
251     }
252
253     /**
254      * Make the PowerFlowRealtimeDataRequest
255      *
256      * @param ip address of the device
257      * @return {PowerFlowRealtimeResponse} the object representation of the json response
258      */
259     private PowerFlowRealtimeResponse getPowerFlowRealtime(String ip) throws FroniusCommunicationException {
260         String location = FroniusBindingConstants.getPowerFlowDataUrl(ip);
261         return collectDataFromUrl(PowerFlowRealtimeResponse.class, location);
262     }
263
264     /**
265      * Make the InverterRealtimeDataRequest
266      *
267      * @param ip address of the device
268      * @param deviceId of the device
269      * @return {InverterRealtimeResponse} the object representation of the json response
270      */
271     private InverterRealtimeResponse getRealtimeData(String ip, int deviceId) throws FroniusCommunicationException {
272         String location = FroniusBindingConstants.getInverterDataUrl(ip, deviceId);
273         return collectDataFromUrl(InverterRealtimeResponse.class, location);
274     }
275
276     /**
277      * Calculate the power value from the given voltage and current channels
278      * 
279      * @param voltage the voltage ValueUnit
280      * @param current the current ValueUnit
281      * @return {QuantityType<>} the power value calculated by multiplying voltage and current
282      */
283     private QuantityType<?> calculatePower(ValueUnit voltage, ValueUnit current) {
284         QuantityType<?> qtyVoltage = getQuantityOrZero(voltage, Units.VOLT);
285         QuantityType<?> qtyCurrent = getQuantityOrZero(current, Units.AMPERE);
286         return qtyVoltage.multiply(qtyCurrent).toUnit(Units.WATT);
287     }
288 }