2 * Copyright (c) 2010-2024 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.time.DateTimeException;
17 import java.time.ZoneId;
18 import java.time.ZonedDateTime;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.core.io.net.http.HttpUtil;
25 import org.openhab.core.library.types.DateTimeType;
26 import org.openhab.core.library.types.QuantityType;
27 import org.openhab.core.library.unit.SIUnits;
28 import org.openhab.core.library.unit.Units;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.binding.BaseThingHandler;
34 import org.openhab.core.types.Command;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import com.google.gson.FieldNamingPolicy;
39 import com.google.gson.Gson;
40 import com.google.gson.GsonBuilder;
43 * The {@link HomeWizardHandler} is responsible for handling commands, which are
44 * sent to one of the channels.
46 * @author Daniƫl van Os - Initial contribution
49 public class HomeWizardHandler extends BaseThingHandler {
51 private final Logger logger = LoggerFactory.getLogger(HomeWizardHandler.class);
52 private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
55 private HomeWizardConfiguration config = new HomeWizardConfiguration();
56 private @Nullable ScheduledFuture<?> pollingJob;
58 private String apiURL = "";
59 private String meterModel = "";
60 private int meterVersion = 0;
65 * @param thing The thing to handle
67 public HomeWizardHandler(Thing thing) {
72 * Not listening to any commands.
75 public void handleCommand(ChannelUID channelUID, Command command) {
79 * If a host has been specified start polling it
82 public void initialize() {
83 config = getConfigAs(HomeWizardConfiguration.class);
85 pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay, TimeUnit.SECONDS);
90 * Check the current configuration
92 * @return true if the configuration is ok to start polling, false otherwise
94 private boolean configure() {
95 if (config.ipAddress.trim().isEmpty()) {
96 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
97 "Missing ipAddress/host configuration");
100 updateStatus(ThingStatus.UNKNOWN);
101 apiURL = String.format("http://%s/api/v1/data", config.ipAddress.trim());
107 * dispose: stop the poller
110 public void dispose() {
111 var job = pollingJob;
119 * The actual polling loop
121 private void pollingCode() {
125 result = HttpUtil.executeUrl("GET", apiURL, 30000);
126 } catch (IOException e) {
127 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
128 String.format("Unable to query P1 Meter: %s", e.getMessage()));
132 if (result.trim().isEmpty()) {
133 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
134 "P1 Meter API returned empty status");
138 P1Payload payload = gson.fromJson(result, P1Payload.class);
139 if (payload == null) {
140 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
141 "Unable to parse response from P1 meter");
145 if ("".equals(payload.getMeterModel())) {
146 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Results from API are empty");
150 updateStatus(ThingStatus.ONLINE);
152 if (!meterModel.equals(payload.getMeterModel())) {
153 meterModel = payload.getMeterModel();
154 updateProperty(HomeWizardBindingConstants.PROPERTY_METER_MODEL, meterModel);
157 if (meterVersion != payload.getSmrVersion()) {
158 meterVersion = payload.getSmrVersion();
159 updateProperty(HomeWizardBindingConstants.PROPERTY_METER_VERSION, String.format("%d", meterVersion));
162 updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T1,
163 new QuantityType<>(payload.getTotalEnergyImportT1Kwh(), Units.KILOWATT_HOUR));
164 updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_IMPORT_T2,
165 new QuantityType<>(payload.getTotalEnergyImportT2Kwh(), Units.KILOWATT_HOUR));
166 updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T1,
167 new QuantityType<>(payload.getTotalEnergyExportT1Kwh(), Units.KILOWATT_HOUR));
168 updateState(HomeWizardBindingConstants.CHANNEL_ENERGY_EXPORT_T2,
169 new QuantityType<>(payload.getTotalEnergyExportT2Kwh(), Units.KILOWATT_HOUR));
171 updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER,
172 new QuantityType<>(payload.getActivePowerW(), Units.WATT));
173 updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L1,
174 new QuantityType<>(payload.getActivePowerL1W(), Units.WATT));
175 updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L2,
176 new QuantityType<>(payload.getActivePowerL2W(), Units.WATT));
177 updateState(HomeWizardBindingConstants.CHANNEL_ACTIVE_POWER_L3,
178 new QuantityType<>(payload.getActivePowerL3W(), Units.WATT));
180 // If no data from the gas meter is present, the json value will be null, which means gson ignores it,
181 // leaving the value in the payload object at 0.
182 long dtv = payload.getGasTimestamp();
184 updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_GAS,
185 new QuantityType<>(payload.getTotalGasM3(), SIUnits.CUBIC_METRE));
188 int seconds = (int) (dtv % 100);
191 int minutes = (int) (dtv % 100);
194 int hours = (int) (dtv % 100);
197 int day = (int) (dtv % 100);
200 int month = (int) (dtv % 100);
203 int year = (int) (dtv + 2000);
206 DateTimeType dtt = new DateTimeType(
207 ZonedDateTime.of(year, month, day, hours, minutes, seconds, 0, ZoneId.systemDefault()));
208 updateState(HomeWizardBindingConstants.CHANNEL_GAS_TIMESTAMP, dtt);
209 updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_GAS,
210 new QuantityType<>(payload.getTotalGasM3(), SIUnits.CUBIC_METRE));
211 } catch (DateTimeException e) {
212 logger.warn("Unable to parse Gas timestamp: {}", payload.getGasTimestamp());