]> git.basschouten.com Git - openhab-addons.git/blob
83cf8b3173f6bb97db9210593d7e31c50e364ad5
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.mecmeter.handler;
14
15 import java.nio.charset.StandardCharsets;
16 import java.util.Base64;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20 import java.util.concurrent.TimeoutException;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.eclipse.jetty.client.HttpClient;
25 import org.eclipse.jetty.client.api.ContentResponse;
26 import org.eclipse.jetty.http.HttpHeader;
27 import org.eclipse.jetty.http.HttpMethod;
28 import org.openhab.binding.mecmeter.MecMeterBindingConstants;
29 import org.openhab.binding.mecmeter.MecMeterDeviceConfiguration;
30 import org.openhab.binding.mecmeter.internal.dto.MecMeterResponse;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.unit.SIUnits;
33 import org.openhab.core.library.unit.Units;
34 import org.openhab.core.thing.Channel;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.binding.BaseThingHandler;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.types.RefreshType;
42 import org.openhab.core.types.State;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import com.google.gson.Gson;
47 import com.google.gson.JsonSyntaxException;
48
49 /**
50  * The {@link MecMeterHandler} is responsible for handling commands, which are
51  * sent to one of the channels.
52  *
53  * @author Florian Pazour - Initial contribution
54  * @author Klaus Berger - Initial contribution
55  * @author Kai Kreuzer - Refactoring for openHAB 3
56  */
57 @NonNullByDefault
58 public class MecMeterHandler extends BaseThingHandler {
59
60     private static final int API_TIMEOUT = 5000; // set on 5000ms - not specified in datasheet
61
62     private static final String USERNAME = "admin";
63
64     private final Logger logger = LoggerFactory.getLogger(MecMeterHandler.class);
65
66     private Gson gson = new Gson();
67
68     private final HttpClient httpClient;
69
70     private @Nullable ScheduledFuture<?> pollFuture;
71
72     private @Nullable MecMeterResponse powerMeterResponse;
73
74     public MecMeterHandler(Thing thing, HttpClient httpClient) {
75         super(thing);
76         this.httpClient = httpClient;
77     }
78
79     @Override
80     public void handleCommand(ChannelUID channelUID, Command command) {
81         if (command instanceof RefreshType) {
82             updateChannel(channelUID.getId());
83         } else {
84             logger.debug("Received unsupported command {}.", command);
85         }
86     }
87
88     /**
89      * function which is called to refresh the data
90      */
91     public void refresh() {
92         updateData();
93         updateChannels();
94     }
95
96     @Override
97     public void dispose() {
98         super.dispose();
99         logger.debug("removing thing..");
100         if (pollFuture != null) {
101             pollFuture.cancel(true);
102         }
103     }
104
105     @Override
106     public void initialize() {
107         MecMeterDeviceConfiguration config = getConfig().as(MecMeterDeviceConfiguration.class);
108         String configCheck = config.isValid();
109
110         if (configCheck != null) {
111             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, configCheck);
112             return;
113         }
114         updateStatus(ThingStatus.UNKNOWN);
115
116         if (pollFuture != null) {
117             pollFuture.cancel(false);
118         }
119         pollFuture = scheduler.scheduleWithFixedDelay(() -> {
120             refresh();
121         }, 0, config.refreshInterval, TimeUnit.SECONDS);
122     }
123
124     /**
125      * Get new data
126      * Function to save Response of the powermeter
127      */
128     private void updateData() {
129         powerMeterResponse = getRealtimeData();
130     }
131
132     /**
133      * Get new realtime data over the network
134      *
135      * @return MecMeterResponse class where json values "are saved"
136      */
137     private @Nullable MecMeterResponse getRealtimeData() {
138         MecMeterResponse result = null;
139         boolean resultOk = false;
140         String errorMsg = null;
141
142         MecMeterDeviceConfiguration config = getConfig().as(MecMeterDeviceConfiguration.class);
143
144         try {
145             String basicAuthentication = "Basic " + Base64.getEncoder()
146                     .encodeToString(new String(USERNAME + ":" + config.password).getBytes(StandardCharsets.ISO_8859_1));
147
148             String location = MecMeterBindingConstants.POWERMETER_DATA_URL.replace("%IP%", config.ip.strip());
149
150             ContentResponse response = httpClient.newRequest(location).method(HttpMethod.GET)
151                     .header(HttpHeader.AUTHORIZATION, basicAuthentication).timeout(API_TIMEOUT, TimeUnit.MILLISECONDS)
152                     .send();
153             if (response.getStatus() != 200) {
154                 errorMsg = "Reading meter did not succeed: " + response.getReason();
155                 logger.error("Request to meter failed: HTTP {}: {}", response.getStatus(), response.getReason());
156             } else {
157                 result = gson.fromJson(response.getContentAsString(), MecMeterResponse.class);
158                 if (result == null) {
159                     errorMsg = "no data returned";
160                     logger.error("no data returned from meter at {}", location);
161                 } else {
162                     resultOk = true;
163                 }
164             }
165         } catch (JsonSyntaxException e) {
166             errorMsg = "Configuration is incorrect";
167             logger.error("Error running power meter request: {}", e.getMessage());
168         } catch (IllegalStateException e) {
169             errorMsg = "Connection failed";
170             logger.error("Error running powermeter request: {}", e.getMessage());
171         } catch (InterruptedException e) {
172             logger.debug("Http request has been interrupted: {}", e.getMessage());
173         } catch (TimeoutException e) {
174             logger.debug("Http request ran into a timeout: {}", e.getMessage());
175             errorMsg = "Connection to power meter timed out.";
176         } catch (ExecutionException e) {
177             logger.debug("Http request did not succeed: {}", e.getMessage());
178             errorMsg = "Connection problem: " + e.getMessage();
179         }
180
181         // Update the thing status
182         if (resultOk) {
183             updateStatus(ThingStatus.ONLINE);
184         } else {
185             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, errorMsg);
186         }
187
188         return resultOk ? result : null;
189     }
190
191     /**
192      * Update all Channels
193      */
194     protected void updateChannels() {
195         for (Channel channel : getThing().getChannels()) {
196             updateChannel(channel.getUID().getId());
197         }
198     }
199
200     /**
201      * Update the channel state
202      *
203      * @param channelId the id identifying the channel to be updated
204      */
205     protected void updateChannel(String channelId) {
206         if (!isLinked(channelId)) {
207             return;
208         }
209         State state = getState(channelId);
210         if (state != null) {
211             updateState(channelId, state);
212         }
213     }
214
215     /**
216      * Get the state of a given channel
217      *
218      * @param channelId the id identifying the channel to be updated
219      * @return state of the channel
220      */
221     protected @Nullable State getState(String channelId) {
222         MecMeterResponse response = powerMeterResponse;
223         if (response == null) {
224             return null;
225         } else {
226             switch (channelId) {
227                 /* General */
228                 case MecMeterBindingConstants.FREQUENCY:
229                     return new QuantityType<>(response.getFrequency(), Units.HERTZ);
230                 case MecMeterBindingConstants.TEMPERATURE:
231                     return new QuantityType<>(response.getTemperature(), SIUnits.CELSIUS);
232                 case MecMeterBindingConstants.OPERATIONAL_TIME:
233                     return new QuantityType<>(response.getOperationalTime() / 1000, Units.SECOND);
234
235                 /* Voltage */
236                 case MecMeterBindingConstants.VOLTAGE_PHASE_1:
237                     return new QuantityType<>(response.getVoltagePhase1(), Units.VOLT);
238                 case MecMeterBindingConstants.VOLTAGE_PHASE_2:
239                     return new QuantityType<>(response.getVoltagePhase2(), Units.VOLT);
240                 case MecMeterBindingConstants.VOLTAGE_PHASE_3:
241                     return new QuantityType<>(response.getVoltagePhase3(), Units.VOLT);
242                 case MecMeterBindingConstants.VOLTAGE_PHASE_3_TO_PHASE_2:
243                     return new QuantityType<>(response.getVoltagePhase3ToPhase2(), Units.VOLT);
244                 case MecMeterBindingConstants.VOLTAGE_PHASE_2_TO_PHASE_1:
245                     return new QuantityType<>(response.getVoltagePhase2ToPhase1(), Units.VOLT);
246                 case MecMeterBindingConstants.VOLTAGE_PHASE_1_TO_PHASE_3:
247                     return new QuantityType<>(response.getVoltagePhase1ToPhase3(), Units.VOLT);
248                 case MecMeterBindingConstants.AVERAGE_VOLTAGE_PHASE_2_PHASE:
249                     return new QuantityType<>(response.getAverageVoltagePhaseToPhase(), Units.VOLT);
250                 case MecMeterBindingConstants.AVERAGE_VOLTAGE_NEUTRAL_2_PHASE:
251                     return new QuantityType<>(response.getAverageVoltageNeutralToPhase(), Units.VOLT);
252
253                 /* Current */
254                 case MecMeterBindingConstants.CURRENT_PHASE_1:
255                     return new QuantityType<>(response.getCurrentPhase1(), Units.AMPERE);
256                 case MecMeterBindingConstants.CURRENT_PHASE_2:
257                     return new QuantityType<>(response.getCurrentPhase2(), Units.AMPERE);
258                 case MecMeterBindingConstants.CURRENT_PHASE_3:
259                     return new QuantityType<>(response.getCurrentPhase3(), Units.AMPERE);
260                 case MecMeterBindingConstants.CURRENT_SUM:
261                     return new QuantityType<>(response.getCurrentSum(), Units.AMPERE);
262
263                 /* Angles */
264                 case MecMeterBindingConstants.PHASE_ANGLE_TO_CURRENT_PHASE_1:
265                     return new QuantityType<>(response.getPhaseAngleCurrentToVoltagePhase1(), Units.DEGREE_ANGLE);
266                 case MecMeterBindingConstants.PHASE_ANGLE_TO_CURRENT_PHASE_2:
267                     return new QuantityType<>(response.getPhaseAngleCurrentToVoltagePhase2(), Units.DEGREE_ANGLE);
268                 case MecMeterBindingConstants.PHASE_ANGLE_TO_CURRENT_PHASE_3:
269                     return new QuantityType<>(response.getPhaseAngleCurrentToVoltagePhase3(), Units.DEGREE_ANGLE);
270                 case MecMeterBindingConstants.PHASE_ANGLE_PHASE_1_3:
271                     return new QuantityType<>(response.getPhaseAnglePhase1To3(), Units.DEGREE_ANGLE);
272                 case MecMeterBindingConstants.PHASE_ANGLE_PHASE_2_3:
273                     return new QuantityType<>(response.getPhaseAnglePhase2To3(), Units.DEGREE_ANGLE);
274
275                 /* Power */
276                 case MecMeterBindingConstants.ACTIVE_POWER_PHASE_1:
277                     return new QuantityType<>(response.getActivePowerPhase1(), Units.WATT);
278                 case MecMeterBindingConstants.ACTIVE_POWER_PHASE_2:
279                     return new QuantityType<>(response.getActivePowerPhase2(), Units.WATT);
280                 case MecMeterBindingConstants.ACTIVE_POWER_PHASE_3:
281                     return new QuantityType<>(response.getActivePowerPhase3(), Units.WATT);
282                 case MecMeterBindingConstants.ACTIVE_POWER_SUM:
283                     return new QuantityType<>(response.getActivePowerSum(), Units.WATT);
284                 case MecMeterBindingConstants.ACTIVE_FUND_POWER_PHASE_1:
285                     return new QuantityType<>(response.getActiveFundamentalPowerPhase1(), Units.WATT);
286                 case MecMeterBindingConstants.ACTIVE_FUND_POWER_PHASE_2:
287                     return new QuantityType<>(response.getActiveFundamentalPowerPhase2(), Units.WATT);
288                 case MecMeterBindingConstants.ACTIVE_FUND_POWER_PHASE_3:
289                     return new QuantityType<>(response.getActiveFundamentalPowerPhase3(), Units.WATT);
290                 case MecMeterBindingConstants.ACTIVE_FUND_POWER_ALL:
291                     return new QuantityType<>(response.getActiveFundamentalPowerSum(), Units.WATT);
292                 case MecMeterBindingConstants.ACTIVE_HARM_POWER_PHASE_1:
293                     return new QuantityType<>(response.getActiveHarmonicPowerPhase1(), Units.WATT);
294                 case MecMeterBindingConstants.ACTIVE_HARM_POWER_PHASE_2:
295                     return new QuantityType<>(response.getActiveHarmonicPowerPhase2(), Units.WATT);
296                 case MecMeterBindingConstants.ACTIVE_HARM_POWER_PHASE_3:
297                     return new QuantityType<>(response.getActiveHarmonicPowerPhase3(), Units.WATT);
298                 case MecMeterBindingConstants.ACTIVE_HARM_POWER_ALL:
299                     return new QuantityType<>(response.getActiveHarmonicPowerSum(), Units.WATT);
300                 case MecMeterBindingConstants.REACTIVE_POWER_PHASE_1:
301                     return new QuantityType<>(response.getReactivePowerPhase1(), Units.VAR);
302                 case MecMeterBindingConstants.REACTIVE_POWER_PHASE_2:
303                     return new QuantityType<>(response.getReactivePowerPhase2(), Units.VAR);
304                 case MecMeterBindingConstants.REACTIVE_POWER_PHASE_3:
305                     return new QuantityType<>(response.getReactivePowerPhase3(), Units.VAR);
306                 case MecMeterBindingConstants.REACTIVE_POWER_ALL:
307                     return new QuantityType<>(response.getReactivePowerSum(), Units.VAR);
308                 case MecMeterBindingConstants.APP_POWER_PHASE_1:
309                     return new QuantityType<>(response.getApparentPowerPhase1(), Units.VOLT_AMPERE);
310                 case MecMeterBindingConstants.APP_POWER_PHASE_2:
311                     return new QuantityType<>(response.getApparentPowerPhase2(), Units.VOLT_AMPERE);
312                 case MecMeterBindingConstants.APP_POWER_PHASE_3:
313                     return new QuantityType<>(response.getApparentPowerPhase3(), Units.VOLT_AMPERE);
314                 case MecMeterBindingConstants.APP_POWER_ALL:
315                     return new QuantityType<>(response.getApparentPowerSum(), Units.VOLT_AMPERE);
316
317                 /* Forward Energy */
318                 case MecMeterBindingConstants.FORWARD_ACTIVE_ENERGY_PHASE_1:
319                     return new QuantityType<>(response.getForwardActiveEnergyPhase1(), Units.KILOWATT_HOUR);
320                 case MecMeterBindingConstants.FORWARD_ACTIVE_ENERGY_PHASE_2:
321                     return new QuantityType<>(response.getForwardActiveEnergyPhase2(), Units.KILOWATT_HOUR);
322                 case MecMeterBindingConstants.FORWARD_ACTIVE_ENERGY_PHASE_3:
323                     return new QuantityType<>(response.getForwardActiveEnergyPhase3(), Units.KILOWATT_HOUR);
324                 case MecMeterBindingConstants.FORWARD_ACTIVE_ENERGY_ALL:
325                     return new QuantityType<>(response.getForwardActiveEnergySum(), Units.KILOWATT_HOUR);
326                 case MecMeterBindingConstants.FORWARD_ACTIVE_FUND_ENERGY_PHASE_1:
327                     return new QuantityType<>(response.getForwardActiveFundamentalEnergyPhase1(), Units.KILOWATT_HOUR);
328                 case MecMeterBindingConstants.FORWARD_ACTIVE_FUND_ENERGY_PHASE_2:
329                     return new QuantityType<>(response.getForwardActiveFundamentalEnergyPhase2(), Units.KILOWATT_HOUR);
330                 case MecMeterBindingConstants.FORWARD_ACTIVE_FUND_ENERGY_PHASE_3:
331                     return new QuantityType<>(response.getForwardActiveFundamentalEnergyPhase3(), Units.KILOWATT_HOUR);
332                 case MecMeterBindingConstants.FORWARD_ACTIVE_FUND_ENERGY_ALL:
333                     return new QuantityType<>(response.getForwardActiveFundamentalEnergySum(), Units.KILOWATT_HOUR);
334                 case MecMeterBindingConstants.FORWARD_ACTIVE_HARM_ENERGY_PHASE_1:
335                     return new QuantityType<>(response.getForwardActiveHarmonicEnergyPhase1(), Units.KILOWATT_HOUR);
336                 case MecMeterBindingConstants.FORWARD_ACTIVE_HARM_ENERGY_PHASE_2:
337                     return new QuantityType<>(response.getForwardActiveHarmonicEnergyPhase2(), Units.KILOWATT_HOUR);
338                 case MecMeterBindingConstants.FORWARD_ACTIVE_HARM_ENERGY_PHASE_3:
339                     return new QuantityType<>(response.getForwardActiveHarmonicEnergyPhase3(), Units.KILOWATT_HOUR);
340                 case MecMeterBindingConstants.FORWARD_ACTIVE_HARM_ENERGY_ALL:
341                     return new QuantityType<>(response.getForwardActiveHarmonicEnergySum(), Units.KILOWATT_HOUR);
342                 case MecMeterBindingConstants.FORWARD_REACTIVE_ENERGY_PHASE_1:
343                     return new QuantityType<>(response.getForwardReactiveEnergyPhase1(), Units.VAR_HOUR);
344                 case MecMeterBindingConstants.FORWARD_REACTIVE_ENERGY_PHASE_2:
345                     return new QuantityType<>(response.getForwardReactiveEnergyPhase2(), Units.VAR_HOUR);
346                 case MecMeterBindingConstants.FORWARD_REACTIVE_ENERGY_PHASE_3:
347                     return new QuantityType<>(response.getForwardReactiveEnergyPhase3(), Units.VAR_HOUR);
348                 case MecMeterBindingConstants.FORWARD_REACTIVE_ENERGY_ALL:
349                     return new QuantityType<>(response.getForwardReactiveEnergySum(), Units.VAR_HOUR);
350
351                 /* Reverse Energy */
352                 case MecMeterBindingConstants.REVERSE_ACTIVE_ENERGY_PHASE_1:
353                     return new QuantityType<>(response.getReverseActiveEnergyPhase1(), Units.KILOWATT_HOUR);
354                 case MecMeterBindingConstants.REVERSE_ACTIVE_ENERGY_PHASE_2:
355                     return new QuantityType<>(response.getReverseActiveEnergyPhase2(), Units.KILOWATT_HOUR);
356                 case MecMeterBindingConstants.REVERSE_ACTIVE_ENERGY_PHASE_3:
357                     return new QuantityType<>(response.getReverseActiveEnergyPhase3(), Units.KILOWATT_HOUR);
358                 case MecMeterBindingConstants.REVERSE_ACTIVE_ENERGY_ALL:
359                     return new QuantityType<>(response.getReverseActiveEnergySum(), Units.KILOWATT_HOUR);
360                 case MecMeterBindingConstants.REVERSE_ACTIVE_FUND_ENERGY_PHASE_1:
361                     return new QuantityType<>(response.getReverseActiveFundamentalEnergyPhase1(), Units.KILOWATT_HOUR);
362                 case MecMeterBindingConstants.REVERSE_ACTIVE_FUND_ENERGY_PHASE_2:
363                     return new QuantityType<>(response.getReverseActiveFundamentalEnergyPhase2(), Units.KILOWATT_HOUR);
364                 case MecMeterBindingConstants.REVERSE_ACTIVE_FUND_ENERGY_PHASE_3:
365                     return new QuantityType<>(response.getReverseActiveFundamentalEnergyPhase3(), Units.KILOWATT_HOUR);
366                 case MecMeterBindingConstants.REVERSE_ACTIVE_FUND_ENERGY_ALL:
367                     return new QuantityType<>(response.getReverseActiveFundamentalEnergySum(), Units.KILOWATT_HOUR);
368                 case MecMeterBindingConstants.REVERSE_ACTIVE_HARM_ENERGY_PHASE_1:
369                     return new QuantityType<>(response.getReverseActiveHarmonicEnergyPhase1(), Units.KILOWATT_HOUR);
370                 case MecMeterBindingConstants.REVERSE_ACTIVE_HARM_ENERGY_PHASE_2:
371                     return new QuantityType<>(response.getReverseActiveHarmonicEnergyPhase2(), Units.KILOWATT_HOUR);
372                 case MecMeterBindingConstants.REVERSE_ACTIVE_HARM_ENERGY_PHASE_3:
373                     return new QuantityType<>(response.getReverseActiveHarmonicEnergyPhase3(), Units.KILOWATT_HOUR);
374                 case MecMeterBindingConstants.REVERSE_ACTIVE_HARM_ENERGY_ALL:
375                     return new QuantityType<>(response.getReverseActiveHarmonicEnergySum(), Units.KILOWATT_HOUR);
376                 case MecMeterBindingConstants.REVERSE_REACTIVE_ENERGY_PHASE_1:
377                     return new QuantityType<>(response.getReverseReactiveEnergyPhase1(), Units.VAR_HOUR);
378                 case MecMeterBindingConstants.REVERSE_REACTIVE_ENERGY_PHASE_2:
379                     return new QuantityType<>(response.getReverseReactiveEnergyPhase2(), Units.VAR_HOUR);
380                 case MecMeterBindingConstants.REVERSE_REACTIVE_ENERGY_PHASE_3:
381                     return new QuantityType<>(response.getReverseReactiveEnergyPhase3(), Units.VAR_HOUR);
382                 case MecMeterBindingConstants.REVERSE_REACTIVE_ENERGY_ALL:
383                     return new QuantityType<>(response.getReverseReactiveEnergySum(), Units.VAR_HOUR);
384
385                 /* Apparent Energy */
386                 case MecMeterBindingConstants.APP_ENERGY_PHASE_1:
387                     return new QuantityType<>(response.getApparentEnergyConsumptionPhase1(), Units.VOLT_AMPERE_HOUR);
388                 case MecMeterBindingConstants.APP_ENERGY_PHASE_2:
389                     return new QuantityType<>(response.getApparentEnergyConsumptionPhase2(), Units.VOLT_AMPERE_HOUR);
390                 case MecMeterBindingConstants.APP_ENERGY_PHASE_3:
391                     return new QuantityType<>(response.getApparentEnergyConsumptionPhase3(), Units.VOLT_AMPERE_HOUR);
392                 case MecMeterBindingConstants.APP_ENERGY_ALL:
393                     return new QuantityType<>(response.getApparentEnergyConsumptionSum(), Units.VOLT_AMPERE_HOUR);
394
395                 /* Power Factor */
396                 case MecMeterBindingConstants.POWER_FACTOR_PHASE_1:
397                     return new QuantityType<>(response.getPowerFactorPhase1(), Units.ONE);
398                 case MecMeterBindingConstants.POWER_FACTOR_PHASE_2:
399                     return new QuantityType<>(response.getPowerFactorPhase2(), Units.ONE);
400                 case MecMeterBindingConstants.POWER_FACTOR_PHASE_3:
401                     return new QuantityType<>(response.getPowerFactorPhase3(), Units.ONE);
402                 case MecMeterBindingConstants.POWER_FACTOR_ALL:
403                     return new QuantityType<>(response.getPowerFactorSum(), Units.ONE);
404             }
405         }
406         return null;
407     }
408 }