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.homewizard.internal;
15 import java.io.IOException;
16 import java.util.concurrent.ScheduledFuture;
17 import java.util.concurrent.TimeUnit;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.core.io.net.http.HttpUtil;
22 import org.openhab.core.library.types.DateTimeType;
23 import org.openhab.core.library.types.QuantityType;
24 import org.openhab.core.library.unit.SIUnits;
25 import org.openhab.core.library.unit.Units;
26 import org.openhab.core.thing.ChannelUID;
27 import org.openhab.core.thing.Thing;
28 import org.openhab.core.thing.ThingStatus;
29 import org.openhab.core.thing.ThingStatusDetail;
30 import org.openhab.core.thing.binding.BaseThingHandler;
31 import org.openhab.core.types.Command;
33 import com.google.gson.FieldNamingPolicy;
34 import com.google.gson.Gson;
35 import com.google.gson.GsonBuilder;
38 * The {@link HomeWizardHandler} is responsible for handling commands, which are
39 * sent to one of the channels.
41 * @author Daniƫl van Os - Initial contribution
44 public class HomeWizardHandler extends BaseThingHandler {
46 private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
49 private HomeWizardConfiguration config = new HomeWizardConfiguration();
50 private @Nullable ScheduledFuture<?> pollingJob;
52 private String apiURL = "";
53 private String meterModel = "";
54 private int meterVersion = 0;
59 * @param thing The thing to handle
61 public HomeWizardHandler(Thing thing) {
66 * Not listening to any commands.
69 public void handleCommand(ChannelUID channelUID, Command command) {
73 * If a host has been specified start polling it
76 public void initialize() {
77 config = getConfigAs(HomeWizardConfiguration.class);
79 pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay, TimeUnit.SECONDS);
84 * Check the current configuration
86 * @return true if the configuration is ok to start polling, false otherwise
88 private boolean configure() {
89 if (config.ipAddress.trim().isEmpty()) {
90 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
91 "Missing ipAddress/host configuration");
94 updateStatus(ThingStatus.UNKNOWN);
95 apiURL = String.format("http://%s/api/v1/data", config.ipAddress.trim());
101 * dispose: stop the poller
104 public void dispose() {
105 var job = pollingJob;
113 * The actual polling loop
115 private void pollingCode() {
119 result = HttpUtil.executeUrl("GET", apiURL, 30000);
120 } catch (IOException e) {
121 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
122 String.format("Unable to query P1 Meter: %s", e.getMessage()));
126 if (result.trim().isEmpty()) {
127 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
128 "P1 Meter API returned empty status");
132 P1Payload payload = gson.fromJson(result, P1Payload.class);
133 if (payload == null) {
134 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
135 "Unable to parse response from P1 meter");
139 if ("".equals(payload.getMeterModel())) {
140 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Results from API are empty");
144 updateStatus(ThingStatus.ONLINE);
146 if (!meterModel.equals(payload.getMeterModel())) {
147 meterModel = payload.getMeterModel();
148 updateProperty(HomeWizardBindingConstants.PROPERTY_METER_MODEL, meterModel);
151 if (meterVersion != payload.getSmrVersion()) {
152 meterVersion = payload.getSmrVersion();
153 updateProperty(HomeWizardBindingConstants.PROPERTY_METER_VERSION, String.format("%d", meterVersion));
156 updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1,
157 new QuantityType<>(payload.getTotalEnergyImportT1Kwh(), Units.KILOWATT_HOUR));
158 updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2,
159 new QuantityType<>(payload.getTotalEnergyImportT2Kwh(), Units.KILOWATT_HOUR));
160 updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1,
161 new QuantityType<>(payload.getTotalEnergyExportT1Kwh(), Units.KILOWATT_HOUR));
162 updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2,
163 new QuantityType<>(payload.getTotalEnergyExportT2Kwh(), Units.KILOWATT_HOUR));
165 updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER,
166 new QuantityType<>(payload.getActivePowerW(), Units.WATT));
167 updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L1,
168 new QuantityType<>(payload.getActivePowerL1W(), Units.WATT));
169 updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L2,
170 new QuantityType<>(payload.getActivePowerL2W(), Units.WATT));
171 updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L3,
172 new QuantityType<>(payload.getActivePowerL3W(), Units.WATT));
174 updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_GAS,
175 new QuantityType<>(payload.getTotalGasM3(), SIUnits.CUBIC_METRE));
178 long dtv = payload.getGasTimestamp();
179 long seconds = dtv % 100;
182 long minutes = dtv % 100;
185 long hours = dtv % 100;
188 long day = dtv % 100;
191 long month = dtv % 100;
194 long year = dtv + 2000; // Where (When?) have I seen this before?
196 DateTimeType dtt = DateTimeType
197 .valueOf(String.format("%04d-%02d-%02dT%02d:%02d:%02d", year, month, day, hours, minutes, seconds));
198 updateState(HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP, dtt);