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.mecmeter.handler;
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;
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;
46 import com.google.gson.Gson;
47 import com.google.gson.JsonSyntaxException;
50 * The {@link MecMeterHandler} is responsible for handling commands, which are
51 * sent to one of the channels.
53 * @author Florian Pazour - Initial contribution
54 * @author Klaus Berger - Initial contribution
55 * @author Kai Kreuzer - Refactoring for openHAB 3
58 public class MecMeterHandler extends BaseThingHandler {
60 private static final int API_TIMEOUT = 5000; // set on 5000ms - not specified in datasheet
62 private static final String USERNAME = "admin";
64 private final Logger logger = LoggerFactory.getLogger(MecMeterHandler.class);
66 private Gson gson = new Gson();
68 private final HttpClient httpClient;
70 private @Nullable ScheduledFuture<?> pollFuture;
72 private @Nullable MecMeterResponse powerMeterResponse;
74 public MecMeterHandler(Thing thing, HttpClient httpClient) {
76 this.httpClient = httpClient;
80 public void handleCommand(ChannelUID channelUID, Command command) {
81 if (command instanceof RefreshType) {
82 updateChannel(channelUID.getId());
84 logger.debug("Received unsupported command {}.", command);
89 * function which is called to refresh the data
91 public void refresh() {
97 public void dispose() {
99 logger.debug("removing thing..");
100 if (pollFuture != null) {
101 pollFuture.cancel(true);
106 public void initialize() {
107 MecMeterDeviceConfiguration config = getConfig().as(MecMeterDeviceConfiguration.class);
108 String configCheck = config.isValid();
110 if (configCheck != null) {
111 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, configCheck);
114 updateStatus(ThingStatus.UNKNOWN);
116 if (pollFuture != null) {
117 pollFuture.cancel(false);
119 pollFuture = scheduler.scheduleWithFixedDelay(() -> {
121 }, 0, config.refreshInterval, TimeUnit.SECONDS);
126 * Function to save Response of the powermeter
128 private void updateData() {
129 powerMeterResponse = getRealtimeData();
133 * Get new realtime data over the network
135 * @return MecMeterResponse class where json values "are saved"
137 private @Nullable MecMeterResponse getRealtimeData() {
138 MecMeterResponse result = null;
139 boolean resultOk = false;
140 String errorMsg = null;
142 MecMeterDeviceConfiguration config = getConfig().as(MecMeterDeviceConfiguration.class);
145 String basicAuthentication = "Basic " + Base64.getEncoder()
146 .encodeToString(new String(USERNAME + ":" + config.password).getBytes(StandardCharsets.ISO_8859_1));
148 String location = MecMeterBindingConstants.POWERMETER_DATA_URL.replace("%IP%", config.ip.strip());
150 ContentResponse response = httpClient.newRequest(location).method(HttpMethod.GET)
151 .header(HttpHeader.AUTHORIZATION, basicAuthentication).timeout(API_TIMEOUT, TimeUnit.MILLISECONDS)
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());
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);
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();
181 // Update the thing status
183 updateStatus(ThingStatus.ONLINE);
185 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, errorMsg);
188 return resultOk ? result : null;
192 * Update all Channels
194 protected void updateChannels() {
195 for (Channel channel : getThing().getChannels()) {
196 updateChannel(channel.getUID().getId());
201 * Update the channel state
203 * @param channelId the id identifying the channel to be updated
205 protected void updateChannel(String channelId) {
206 if (!isLinked(channelId)) {
209 State state = getState(channelId);
211 updateState(channelId, state);
216 * Get the state of a given channel
218 * @param channelId the id identifying the channel to be updated
219 * @return state of the channel
221 protected @Nullable State getState(String channelId) {
222 MecMeterResponse response = powerMeterResponse;
223 if (response == null) {
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);
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);
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);
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);
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);
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);
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);
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);
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);