2 * Copyright (c) 2010-2023 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.valloxmv.internal;
15 import java.math.BigDecimal;
16 import java.util.concurrent.ScheduledFuture;
17 import java.util.concurrent.TimeUnit;
19 import javax.measure.quantity.Dimensionless;
20 import javax.measure.quantity.Temperature;
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;
40 * The {@link ValloxMVHandler} is responsible for handling commands, which are
41 * sent to one of the channels.
43 * @author Björn Brings - Initial contribution
46 public class ValloxMVHandler extends BaseThingHandler {
48 private final Logger logger = LoggerFactory.getLogger(ValloxMVHandler.class);
49 private @Nullable ScheduledFuture<?> readDataJob;
50 private @Nullable ValloxMVWebSocket valloxSocket;
51 private @Nullable WebSocketClient webSocketClient;
54 * Refresh interval in seconds.
56 private int readDataInterval;
59 * IP of vallox ventilation unit web interface.
61 public ValloxMVHandler(Thing thing, @Nullable WebSocketClient webSocketClient) {
63 this.webSocketClient = webSocketClient;
66 @SuppressWarnings({ "null", "unchecked" })
68 public void handleCommand(ChannelUID channelUID, Command command) {
69 if (valloxSocket == null) {
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
80 // Schedule read data job with 2 seconds initial delay
81 scheduleReadDataJob(2);
85 String strUpdateValue = "";
86 if (ValloxMVBindingConstants.CHANNEL_STATE.equals(channelUID.getId())) {
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();
96 } catch (NumberFormatException nfe) {
97 // Other commands like refresh
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";
105 // Switches with ON = 1, OFF = 0
106 strUpdateValue = (OnOffType.ON.equals(command)) ? "1" : "0";
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());
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) {
120 int centiKelvin = quantity.multiply(new BigDecimal(100)).intValue();
121 strUpdateValue = Integer.toString(centiKelvin);
124 // Not writable channel
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
135 // Schedule read data job with 5 seconds initial delay
136 scheduleReadDataJob(5);
139 // Send command and process response
140 valloxSocket.request(channelUID, strUpdateValue);
146 public void initialize() {
147 logger.debug("Initializing thing {}", getThing().getUID());
149 updateStatus(ThingStatus.UNKNOWN);
151 String ip = getConfigAs(ValloxMVConfig.class).getIp();
152 valloxSocket = new ValloxMVWebSocket(webSocketClient, ValloxMVHandler.this, ip);
154 logger.debug("Vallox MV IP address : {}", ip);
155 // Schedule read data job with 2 seconds initial delay
156 scheduleReadDataJob(2);
158 logger.debug("Thing {} initialized", getThing().getUID());
161 private void scheduleReadDataJob(int initialDelay) {
162 if (initialDelay < 0) {
166 readDataInterval = getConfigAs(ValloxMVConfig.class).getUpdateinterval();
167 if (readDataInterval < 15) {
168 readDataInterval = 60;
171 logger.debug("Data table request interval {} seconds, Request in {} seconds", readDataInterval, initialDelay);
173 readDataJob = scheduler.scheduleWithFixedDelay(() -> {
174 // Read all device values
175 valloxSocket.request(null, null);
176 }, initialDelay, readDataInterval, TimeUnit.SECONDS);
179 private void cancelReadDataJob() {
180 if (readDataJob != null) {
181 if (!readDataJob.isDone()) {
182 readDataJob.cancel(true);
183 logger.debug("Scheduled data table requests cancelled");
189 public void dispose() {
190 logger.debug("Disposing thing {}", getThing().getUID());
192 logger.debug("Thing {} disposed", getThing().getUID());
196 protected void updateState(String strChannelName, State dt) {
197 super.updateState(strChannelName, dt);
201 protected void updateStatus(ThingStatus ts, ThingStatusDetail statusDetail) {
202 super.updateStatus(ts, statusDetail);
206 protected void updateStatus(ThingStatus ts) {
207 super.updateStatus(ts);