]> git.basschouten.com Git - openhab-addons.git/blob
289f8c13bfdaef8af6ecbbf690cfed96db28d6fb
[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.homewizard.internal;
14
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;
21
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;
37
38 import com.google.gson.FieldNamingPolicy;
39 import com.google.gson.Gson;
40 import com.google.gson.GsonBuilder;
41
42 /**
43  * The {@link HomeWizardHandler} is responsible for handling commands, which are
44  * sent to one of the channels.
45  *
46  * @author DaniĆ«l van Os - Initial contribution
47  */
48 @NonNullByDefault
49 public class HomeWizardHandler extends BaseThingHandler {
50
51     private final Logger logger = LoggerFactory.getLogger(HomeWizardHandler.class);
52     private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
53             .create();
54
55     private HomeWizardConfiguration config = new HomeWizardConfiguration();
56     private @Nullable ScheduledFuture<?> pollingJob;
57
58     private String apiURL = "";
59     private String meterModel = "";
60     private int meterVersion = 0;
61
62     /**
63      * Constructor
64      *
65      * @param thing The thing to handle
66      */
67     public HomeWizardHandler(Thing thing) {
68         super(thing);
69     }
70
71     /**
72      * Not listening to any commands.
73      */
74     @Override
75     public void handleCommand(ChannelUID channelUID, Command command) {
76     }
77
78     /**
79      * If a host has been specified start polling it
80      */
81     @Override
82     public void initialize() {
83         config = getConfigAs(HomeWizardConfiguration.class);
84         if (configure()) {
85             pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, config.refreshDelay, TimeUnit.SECONDS);
86         }
87     }
88
89     /**
90      * Check the current configuration
91      *
92      * @return true if the configuration is ok to start polling, false otherwise
93      */
94     private boolean configure() {
95         if (config.ipAddress.trim().isEmpty()) {
96             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
97                     "Missing ipAddress/host configuration");
98             return false;
99         } else {
100             updateStatus(ThingStatus.UNKNOWN);
101             apiURL = String.format("http://%s/api/v1/data", config.ipAddress.trim());
102             return true;
103         }
104     }
105
106     /**
107      * dispose: stop the poller
108      */
109     @Override
110     public void dispose() {
111         var job = pollingJob;
112         if (job != null) {
113             job.cancel(true);
114         }
115         pollingJob = null;
116     }
117
118     /**
119      * The actual polling loop
120      */
121     private void pollingCode() {
122         final String result;
123
124         try {
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()));
129             return;
130         }
131
132         if (result.trim().isEmpty()) {
133             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
134                     "P1 Meter API returned empty status");
135             return;
136         }
137
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");
142             return;
143         }
144
145         if ("".equals(payload.getMeterModel())) {
146             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Results from API are empty");
147             return;
148         }
149
150         updateStatus(ThingStatus.ONLINE);
151
152         if (!meterModel.equals(payload.getMeterModel())) {
153             meterModel = payload.getMeterModel();
154             updateProperty(HomeWizardBindingConstants.PROPERTY_METER_MODEL, meterModel);
155         }
156
157         if (meterVersion != payload.getSmrVersion()) {
158             meterVersion = payload.getSmrVersion();
159             updateProperty(HomeWizardBindingConstants.PROPERTY_METER_VERSION, String.format("%d", meterVersion));
160         }
161
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));
170
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));
179
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();
183         if (dtv > 0) {
184             updateState(HomeWizardBindingConstants.CHANNEL_TOTAL_GAS,
185                     new QuantityType<>(payload.getTotalGasM3(), SIUnits.CUBIC_METRE));
186
187             // 210119164000
188             int seconds = (int) (dtv % 100);
189
190             dtv /= 100;
191             int minutes = (int) (dtv % 100);
192
193             dtv /= 100;
194             int hours = (int) (dtv % 100);
195
196             dtv /= 100;
197             int day = (int) (dtv % 100);
198
199             dtv /= 100;
200             int month = (int) (dtv % 100);
201
202             dtv /= 100;
203             int year = (int) (dtv + 2000);
204
205             try {
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());
213             }
214         }
215     }
216 }