]> git.basschouten.com Git - openhab-addons.git/blob
0bdd202b1e3a4949d293e5f1afee7c39e40cb94f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.valloxmv.internal;
14
15 import java.math.BigDecimal;
16 import java.util.concurrent.ScheduledFuture;
17 import java.util.concurrent.TimeUnit;
18
19 import javax.measure.quantity.Dimensionless;
20 import javax.measure.quantity.Temperature;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.eclipse.jetty.websocket.client.WebSocketClient;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.library.types.QuantityType;
27 import org.openhab.core.library.unit.Units;
28 import org.openhab.core.thing.ChannelUID;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusDetail;
32 import org.openhab.core.thing.binding.BaseThingHandler;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.RefreshType;
35 import org.openhab.core.types.State;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * The {@link ValloxMVHandler} is responsible for handling commands, which are
41  * sent to one of the channels.
42  *
43  * @author Björn Brings - Initial contribution
44  */
45 @NonNullByDefault
46 public class ValloxMVHandler extends BaseThingHandler {
47
48     private final Logger logger = LoggerFactory.getLogger(ValloxMVHandler.class);
49     private @Nullable ScheduledFuture<?> readDataJob;
50     private @Nullable ValloxMVWebSocket valloxSocket;
51     private @Nullable WebSocketClient webSocketClient;
52
53     /**
54      * Refresh interval in seconds.
55      */
56     private int readDataInterval;
57
58     /**
59      * IP of vallox ventilation unit web interface.
60      */
61     public ValloxMVHandler(Thing thing, @Nullable WebSocketClient webSocketClient) {
62         super(thing);
63         this.webSocketClient = webSocketClient;
64     }
65
66     @SuppressWarnings({ "null", "unchecked" })
67     @Override
68     public void handleCommand(ChannelUID channelUID, Command command) {
69         if (valloxSocket == null) {
70             return;
71         }
72         if (command instanceof RefreshType) {
73             // We don't have Vallox device values in memory - we cannot update channel with value
74             // We can re-schedule readDataJob in case next read is more than 5 seconds away
75             if (readDataJob != null) {
76                 if ((!readDataJob.isDone()) && (readDataJob.getDelay(TimeUnit.MILLISECONDS) > 5000)) {
77                     // Next read is more than 5 seconds away, re-schedule
78                     // Cancel read data job
79                     cancelReadDataJob();
80                     // Schedule read data job with 2 seconds initial delay
81                     scheduleReadDataJob(2);
82                 }
83             }
84         } else {
85             String strUpdateValue = "";
86             if (ValloxMVBindingConstants.CHANNEL_STATE.equals(channelUID.getId())) {
87                 try {
88                     int cmd = Integer.parseInt(command.toString());
89                     if ((cmd == ValloxMVBindingConstants.STATE_FIREPLACE)
90                             || (cmd == ValloxMVBindingConstants.STATE_ATHOME)
91                             || (cmd == ValloxMVBindingConstants.STATE_AWAY)
92                             || (cmd == ValloxMVBindingConstants.STATE_BOOST)) {
93                         // logger.debug("Changing state to: {}", command);
94                         strUpdateValue = command.toString();
95                     }
96                 } catch (NumberFormatException nfe) {
97                     // Other commands like refresh
98                     return;
99                 }
100             } else if (ValloxMVBindingConstants.WRITABLE_CHANNELS_SWITCHES.contains(channelUID.getId())) {
101                 if (ValloxMVBindingConstants.CHANNEL_ONOFF.equals(channelUID.getId())) {
102                     // Vallox MV MODE: Normal mode = 0, Switch off = 5
103                     strUpdateValue = (OnOffType.ON.equals(command)) ? "0" : "5";
104                 } else {
105                     // Switches with ON = 1, OFF = 0
106                     strUpdateValue = (OnOffType.ON.equals(command)) ? "1" : "0";
107                 }
108             } else if (ValloxMVBindingConstants.WRITABLE_CHANNELS_DIMENSIONLESS.contains(channelUID.getId())) {
109                 if (command instanceof QuantityType) {
110                     QuantityType<Dimensionless> quantity = (QuantityType<Dimensionless>) command;
111                     strUpdateValue = Integer.toString(quantity.intValue());
112                 }
113             } else if (ValloxMVBindingConstants.WRITABLE_CHANNELS_TEMPERATURE.contains(channelUID.getId())) {
114                 if (command instanceof QuantityType) {
115                     // Convert temperature to centiKelvin (= (Celsius * 100) + 27315 )
116                     QuantityType<Temperature> quantity = ((QuantityType<Temperature>) command).toUnit(Units.KELVIN);
117                     if (quantity == null) {
118                         return;
119                     }
120                     int centiKelvin = quantity.multiply(new BigDecimal(100)).intValue();
121                     strUpdateValue = Integer.toString(centiKelvin);
122                 }
123             } else {
124                 // Not writable channel
125                 return;
126             }
127             if (!strUpdateValue.isEmpty()) {
128                 if (readDataJob != null) {
129                     // Re-schedule readDataJob to read device values after data write
130                     // Avoid re-scheduling job several times in case of subsequent data writes
131                     long timeToRead = readDataJob.getDelay(TimeUnit.MILLISECONDS);
132                     if ((!readDataJob.isDone()) && ((timeToRead < 2000) || (timeToRead > 5000))) {
133                         // Next read is not within the next 2 to 5 seconds, cancel read data job
134                         cancelReadDataJob();
135                         // Schedule read data job with 5 seconds initial delay
136                         scheduleReadDataJob(5);
137                     }
138                 }
139                 // Send command and process response
140                 valloxSocket.request(channelUID, strUpdateValue);
141             }
142         }
143     }
144
145     @Override
146     public void initialize() {
147         logger.debug("Initializing thing {}", getThing().getUID());
148
149         updateStatus(ThingStatus.UNKNOWN);
150
151         String ip = getConfigAs(ValloxMVConfig.class).getIp();
152         valloxSocket = new ValloxMVWebSocket(webSocketClient, ValloxMVHandler.this, ip);
153
154         logger.debug("Vallox MV IP address : {}", ip);
155         // Schedule read data job with 2 seconds initial delay
156         scheduleReadDataJob(2);
157
158         logger.debug("Thing {} initialized", getThing().getUID());
159     }
160
161     private void scheduleReadDataJob(int initialDelay) {
162         if (initialDelay < 0) {
163             initialDelay = 0;
164         }
165
166         readDataInterval = getConfigAs(ValloxMVConfig.class).getUpdateinterval();
167         if (readDataInterval < 15) {
168             readDataInterval = 60;
169         }
170
171         logger.debug("Data table request interval {} seconds, Request in {} seconds", readDataInterval, initialDelay);
172
173         readDataJob = scheduler.scheduleWithFixedDelay(() -> {
174             // Read all device values
175             valloxSocket.request(null, null);
176         }, initialDelay, readDataInterval, TimeUnit.SECONDS);
177     }
178
179     private void cancelReadDataJob() {
180         if (readDataJob != null) {
181             if (!readDataJob.isDone()) {
182                 readDataJob.cancel(true);
183                 logger.debug("Scheduled data table requests cancelled");
184             }
185         }
186     }
187
188     @Override
189     public void dispose() {
190         logger.debug("Disposing thing {}", getThing().getUID());
191         cancelReadDataJob();
192         logger.debug("Thing {} disposed", getThing().getUID());
193     }
194
195     @Override
196     protected void updateState(String strChannelName, State dt) {
197         super.updateState(strChannelName, dt);
198     }
199
200     @Override
201     protected void updateStatus(ThingStatus ts, ThingStatusDetail statusDetail) {
202         super.updateStatus(ts, statusDetail);
203     }
204
205     @Override
206     protected void updateStatus(ThingStatus ts) {
207         super.updateStatus(ts);
208     }
209 }