]> git.basschouten.com Git - openhab-addons.git/blob
396c37c20d75ef91220f34b79c7c1195c019a3ab
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.ventaair.internal;
14
15 import java.io.IOException;
16 import java.math.BigDecimal;
17 import java.util.HashMap;
18 import java.util.Map;
19
20 import javax.measure.Unit;
21 import javax.measure.quantity.Dimensionless;
22 import javax.measure.quantity.Temperature;
23 import javax.measure.quantity.Time;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.ventaair.internal.message.action.Action;
28 import org.openhab.binding.ventaair.internal.message.action.AllActions;
29 import org.openhab.binding.ventaair.internal.message.action.AutomaticAction;
30 import org.openhab.binding.ventaair.internal.message.action.BoostAction;
31 import org.openhab.binding.ventaair.internal.message.action.ChildLockAction;
32 import org.openhab.binding.ventaair.internal.message.action.FanAction;
33 import org.openhab.binding.ventaair.internal.message.action.HumidityAction;
34 import org.openhab.binding.ventaair.internal.message.action.PowerAction;
35 import org.openhab.binding.ventaair.internal.message.action.SleepModeAction;
36 import org.openhab.binding.ventaair.internal.message.action.TimerAction;
37 import org.openhab.binding.ventaair.internal.message.dto.DeviceInfoMessage;
38 import org.openhab.binding.ventaair.internal.message.dto.Header;
39 import org.openhab.binding.ventaair.internal.message.dto.Info;
40 import org.openhab.binding.ventaair.internal.message.dto.Measurements;
41 import org.openhab.core.library.types.DecimalType;
42 import org.openhab.core.library.types.OnOffType;
43 import org.openhab.core.library.types.QuantityType;
44 import org.openhab.core.library.unit.ImperialUnits;
45 import org.openhab.core.library.unit.SIUnits;
46 import org.openhab.core.library.unit.Units;
47 import org.openhab.core.thing.ChannelUID;
48 import org.openhab.core.thing.Thing;
49 import org.openhab.core.thing.ThingStatus;
50 import org.openhab.core.thing.ThingStatusDetail;
51 import org.openhab.core.thing.binding.BaseThingHandler;
52 import org.openhab.core.types.Command;
53 import org.openhab.core.types.RefreshType;
54 import org.openhab.core.types.State;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * The {@link VentaThingHandler} is responsible for handling commands, which are
60  * sent to one of the channels.
61  *
62  * @author Stefan Triller - Initial contribution
63  */
64 @NonNullByDefault
65 public class VentaThingHandler extends BaseThingHandler {
66
67     private final Logger logger = LoggerFactory.getLogger(VentaThingHandler.class);
68
69     private VentaAirDeviceConfiguration config = new VentaAirDeviceConfiguration();
70
71     private @Nullable Communicator communicator;
72
73     private Map<String, State> channelValueCache = new HashMap<>();
74
75     public VentaThingHandler(Thing thing) {
76         super(thing);
77     }
78
79     @Override
80     public void handleCommand(ChannelUID channelUID, Command command) {
81         logger.debug("Handle command={} for channel={} with channelID={}", command, channelUID, channelUID.getId());
82         if (command instanceof RefreshType) {
83             refreshChannelFromCache(channelUID);
84             return;
85         }
86
87         switch (channelUID.getId()) {
88             case VentaAirBindingConstants.CHANNEL_POWER:
89                 if (command instanceof OnOffType) {
90                     dispatchActionToDevice(new PowerAction(command == OnOffType.ON));
91                 }
92                 break;
93             case VentaAirBindingConstants.CHANNEL_FAN_SPEED:
94                 if (command instanceof DecimalType) {
95                     int fanStage = ((DecimalType) command).intValue();
96                     dispatchActionToDevice(new FanAction(fanStage));
97                 }
98                 break;
99             case VentaAirBindingConstants.CHANNEL_TARGET_HUMIDITY:
100                 if (command instanceof DecimalType) {
101                     int targetHumidity = ((DecimalType) command).intValue();
102                     dispatchActionToDevice(new HumidityAction(targetHumidity));
103                 }
104                 break;
105             case VentaAirBindingConstants.CHANNEL_TIMER:
106                 if (command instanceof DecimalType) {
107                     int timer = ((DecimalType) command).intValue();
108                     dispatchActionToDevice(new TimerAction(timer));
109                 }
110                 break;
111             case VentaAirBindingConstants.CHANNEL_SLEEP_MODE:
112                 if (command instanceof OnOffType) {
113                     dispatchActionToDevice(new SleepModeAction(command == OnOffType.ON));
114                 }
115                 break;
116             case VentaAirBindingConstants.CHANNEL_BOOST:
117                 if (command instanceof OnOffType) {
118                     dispatchActionToDevice(new BoostAction(command == OnOffType.ON));
119                 }
120                 break;
121             case VentaAirBindingConstants.CHANNEL_CHILD_LOCK:
122                 if (command instanceof OnOffType) {
123                     dispatchActionToDevice(new ChildLockAction(command == OnOffType.ON));
124                 }
125                 break;
126             case VentaAirBindingConstants.CHANNEL_AUTOMATIC:
127                 if (command instanceof OnOffType) {
128                     dispatchActionToDevice(new AutomaticAction(command == OnOffType.ON));
129                 }
130                 break;
131
132             default:
133                 break;
134         }
135     }
136
137     @Override
138     public void initialize() {
139         config = getConfigAs(VentaAirDeviceConfiguration.class);
140
141         updateStatus(ThingStatus.UNKNOWN);
142
143         String configErrorMessage;
144         if ((configErrorMessage = validateConfig()) != null) {
145             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, configErrorMessage);
146             return;
147         }
148
149         Header header = new Header(config.macAddress, config.deviceType.intValue(), config.hash.toString(), "openHAB");
150
151         communicator = new Communicator(config.ipAddress, header, config.pollingTime, new StateUpdatedCallback());
152         communicator.startPollDataFromDevice(scheduler);
153     }
154
155     private @Nullable String validateConfig() {
156         if (config.ipAddress.isEmpty()) {
157             return "IP address not set";
158         }
159         if (config.macAddress.isEmpty()) {
160             return "Mac Address not set, use discovery to find the correct one";
161         }
162         if (BigDecimal.ZERO.equals(config.deviceType)) {
163             return "Device Type not set, use discovery to find the correct one";
164         }
165         if (config.pollingTime.compareTo(BigDecimal.ZERO) <= 0) {
166             return "Polling time has to be larger than 0 seconds";
167         }
168
169         return null;
170     }
171
172     private void dispatchActionToDevice(Action action) {
173         Communicator localCommunicator = communicator;
174         if (localCommunicator != null) {
175             logger.debug("Dispatching Action={} to the device", action.getClass());
176             try {
177                 localCommunicator.sendActionToDevice(action);
178             } catch (IOException e) {
179                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
180                 return;
181             }
182             localCommunicator.pollDataFromDevice();
183         } else {
184             logger.error("Should send action={} to device but communicator is not available.", action.getClass());
185         }
186     }
187
188     private void refreshChannelFromCache(ChannelUID channelUID) {
189         State cachedState = channelValueCache.get(channelUID.getId());
190         if (cachedState != null) {
191             updateState(channelUID, cachedState);
192         }
193     }
194
195     private void updateProperties(Info info) {
196         Thing thing = getThing();
197         thing.setProperty("SWDisplay", info.getSwDisplay());
198         thing.setProperty("SWPower", info.getSwPower());
199         thing.setProperty("SWTouch", info.getSwTouch());
200         thing.setProperty("SWWIFI", info.getSwWIFI());
201     }
202
203     @Override
204     public void dispose() {
205         Communicator localCommunicator = communicator;
206         if (localCommunicator != null) {
207             localCommunicator.stopPollDataFromDevice();
208         }
209         communicator = null;
210     }
211
212     class StateUpdatedCallback {
213         /**
214          * Method to pass the data received from the device to the handler
215          *
216          * @param message - message containing the parsed data from the device
217          */
218         public void stateUpdated(DeviceInfoMessage message) {
219             if (messageIsEmpty(message)) {
220                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
221                         "Please allow openHAB to access your device");
222                 return;
223             }
224
225             AllActions actions = message.getCurrentActions();
226
227             Unit<Temperature> temperatureUnit = SIUnits.CELSIUS;
228
229             if (actions != null) {
230                 OnOffType powerState = OnOffType.from(actions.isPower());
231                 updateState(VentaAirBindingConstants.CHANNEL_POWER, powerState);
232                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_POWER, powerState);
233
234                 DecimalType fanspeedState = new DecimalType(actions.getFanSpeed());
235                 updateState(VentaAirBindingConstants.CHANNEL_FAN_SPEED, fanspeedState);
236                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_FAN_SPEED, fanspeedState);
237
238                 DecimalType targetHumState = new DecimalType(actions.getTargetHum());
239                 updateState(VentaAirBindingConstants.CHANNEL_TARGET_HUMIDITY, targetHumState);
240                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_TARGET_HUMIDITY, targetHumState);
241
242                 DecimalType timerState = new DecimalType(actions.getTimer());
243                 updateState(VentaAirBindingConstants.CHANNEL_TIMER, timerState);
244                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_TIMER, timerState);
245
246                 OnOffType sleepModeState = OnOffType.from(actions.isSleepMode());
247                 updateState(VentaAirBindingConstants.CHANNEL_SLEEP_MODE, sleepModeState);
248                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_SLEEP_MODE, sleepModeState);
249
250                 OnOffType boostState = OnOffType.from(actions.isBoost());
251                 updateState(VentaAirBindingConstants.CHANNEL_BOOST, boostState);
252                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_BOOST, boostState);
253
254                 OnOffType childLockState = OnOffType.from(actions.isChildLock());
255                 updateState(VentaAirBindingConstants.CHANNEL_CHILD_LOCK, childLockState);
256                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_CHILD_LOCK, childLockState);
257
258                 OnOffType automaticState = OnOffType.from(actions.isAutomatic());
259                 updateState(VentaAirBindingConstants.CHANNEL_AUTOMATIC, automaticState);
260                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_AUTOMATIC, automaticState);
261
262                 temperatureUnit = actions.getTempUnit() == 0 ? SIUnits.CELSIUS : ImperialUnits.FAHRENHEIT;
263             }
264
265             Measurements measurements = message.getMeasurements();
266
267             if (measurements != null) {
268                 QuantityType<Temperature> temperatureState = new QuantityType<>(measurements.getTemperature(),
269                         temperatureUnit);
270                 updateState(VentaAirBindingConstants.CHANNEL_TEMPERATURE, temperatureState);
271                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_TEMPERATURE, temperatureState);
272
273                 QuantityType<Dimensionless> humidityState = new QuantityType<>(measurements.getHumidity(),
274                         Units.PERCENT);
275                 updateState(VentaAirBindingConstants.CHANNEL_HUMIDITY, humidityState);
276                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_HUMIDITY, humidityState);
277
278                 DecimalType waterLevelState = new DecimalType(measurements.getWaterLevel());
279                 updateState(VentaAirBindingConstants.CHANNEL_WATERLEVEL, waterLevelState);
280                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_WATERLEVEL, waterLevelState);
281
282                 DecimalType fanRPMstate = new DecimalType(measurements.getFanRpm());
283                 updateState(VentaAirBindingConstants.CHANNEL_FAN_RPM, fanRPMstate);
284                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_FAN_RPM, fanRPMstate);
285             }
286
287             Info info = message.getInfo();
288             if (info != null) {
289                 int opHours = info.getOperationT() * 5 / 60;
290                 int discReplaceHours = info.getDiscIonT() * 5 / 60;
291                 int cleaningHours = info.getCleaningT() * 5 / 60;
292
293                 QuantityType<Time> opHoursState = new QuantityType<Time>(opHours, Units.HOUR);
294                 updateState(VentaAirBindingConstants.CHANNEL_OPERATION_TIME, opHoursState);
295                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_OPERATION_TIME, opHoursState);
296
297                 QuantityType<Time> discReplaceHoursState = new QuantityType<Time>(2200 - discReplaceHours, Units.HOUR);
298                 updateState(VentaAirBindingConstants.CHANNEL_DISC_REPLACE_TIME, discReplaceHoursState);
299                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_DISC_REPLACE_TIME, discReplaceHoursState);
300
301                 QuantityType<Time> cleaningHoursState = new QuantityType<Time>(4400 - cleaningHours, Units.HOUR);
302                 updateState(VentaAirBindingConstants.CHANNEL_CLEANING_TIME, cleaningHoursState);
303                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_CLEANING_TIME, cleaningHoursState);
304
305                 OnOffType cleanModeState = info.isCleanMode() ? OnOffType.ON : OnOffType.OFF;
306                 updateState(VentaAirBindingConstants.CHANNEL_CLEAN_MODE, cleanModeState);
307                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_CLEAN_MODE, cleanModeState);
308
309                 QuantityType<Time> timerTimePassedState = new QuantityType<Time>(info.getTimerT(), Units.MINUTE);
310                 updateState(VentaAirBindingConstants.CHANNEL_TIMER_TIME_PASSED, timerTimePassedState);
311                 channelValueCache.put(VentaAirBindingConstants.CHANNEL_TIMER_TIME_PASSED, timerTimePassedState);
312
313                 updateProperties(info);
314             }
315
316             updateStatus(ThingStatus.ONLINE);
317         }
318
319         private boolean messageIsEmpty(DeviceInfoMessage message) {
320             if (message.getCurrentActions() == null && message.getInfo() == null && message.getMeasurements() == null) {
321                 return true;
322             }
323             return false;
324         }
325
326         /**
327          * Method to inform the handler about a communication issue
328          */
329         public void communicationProblem() {
330             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
331         }
332     }
333 }