]> git.basschouten.com Git - openhab-addons.git/blob
6878c59ca0626b237f29e2dc8f8dcf706a39b994
[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.nikohomecontrol.internal.protocol.nhc1;
14
15 import static org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.*;
16
17 import java.util.concurrent.ScheduledExecutorService;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction;
24 import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
25 import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30  * The {@link NhcAction1} class represents the action Niko Home Control I communication object. It contains all fields
31  * representing a Niko Home Control action and has methods to trigger the action in Niko Home Control and receive action
32  * updates.
33  *
34  * @author Mark Herwege - Initial Contribution
35  */
36 @NonNullByDefault
37 public class NhcAction1 extends NhcAction {
38
39     private final Logger logger = LoggerFactory.getLogger(NhcAction1.class);
40
41     @FunctionalInterface
42     private interface Action {
43         void execute();
44     }
45
46     private ScheduledExecutorService scheduler;
47
48     private volatile @Nullable Action rollershutterTask;
49     private volatile @Nullable ScheduledFuture<?> rollershutterStopTask;
50     private volatile @Nullable ScheduledFuture<?> rollershutterMovingFlagTask;
51
52     private volatile boolean filterEvent = false; // flag to filter first event from rollershutter on percent move to
53                                                   // avoid wrong position update
54     private volatile boolean rollershutterMoving = false; // flag to indicate if rollershutter is currently moving
55     private volatile boolean waitForEvent = false; // flag to wait for position update rollershutter before doing next
56                                                    // move
57
58     NhcAction1(String id, String name, ActionType type, @Nullable String location, NikoHomeControlCommunication nhcComm,
59             ScheduledExecutorService scheduler) {
60         super(id, name, type, location, nhcComm);
61         this.scheduler = scheduler;
62     }
63
64     /**
65      * Sets state of action. This is the version for Niko Home Control I.
66      *
67      * @param newState - The allowed values depend on the action type.
68      *            switch action: 0 or 100
69      *            dimmer action: between 0 and 100
70      *            rollershutter action: between 0 and 100
71      */
72     @Override
73     public void setState(int newState) {
74         if (getType() == ActionType.ROLLERSHUTTER) {
75             if (filterEvent) {
76                 filterEvent = false;
77                 logger.debug("filtered event {} for {}", newState, id);
78                 return;
79             }
80
81             cancelRollershutterStop();
82
83             if (((newState == 0) || (newState == 100)) && (newState != state)) {
84                 long duration = rollershutterMoveTime(state, newState);
85                 setRollershutterMovingTrue(duration);
86             } else {
87                 setRollershutterMovingFalse();
88             }
89         }
90         if (waitForEvent) {
91             logger.debug("received requested rollershutter {} position event {}", id, newState);
92             executeRollershutterTask();
93         } else {
94             state = newState;
95             updateState();
96         }
97     }
98
99     /**
100      * Sends action to Niko Home Control. This version is used for Niko Home Control I.
101      *
102      * @param command - The allowed values depend on the action type.
103      */
104     @Override
105     public void execute(String command) {
106         logger.debug("execute action {} of type {} for {}", command, type, id);
107
108         String value = "";
109         switch (getType()) {
110             case GENERIC:
111             case TRIGGER:
112             case RELAY:
113                 if (command.equals(NHCON)) {
114                     value = "100";
115                 } else {
116                     value = "0";
117                 }
118                 nhcComm.executeAction(id, value);
119                 break;
120             case DIMMER:
121                 if (command.equals(NHCON)) {
122                     value = "254";
123                 } else if (command.equals(NHCOFF)) {
124                     value = "255";
125                 } else {
126                     value = command;
127                 }
128                 nhcComm.executeAction(id, value);
129                 break;
130             case ROLLERSHUTTER:
131                 executeRollershutter(command);
132                 break;
133         }
134     }
135
136     private void executeRollershutter(String command) {
137         if (logger.isTraceEnabled()) {
138             logger.trace("handleRollerShutterCommand: rollershutter {} command {}", id, command);
139             logger.trace("handleRollerShutterCommand: rollershutter {}, current position {}", id, state);
140         }
141
142         // first stop all current movement of rollershutter and wait until exact position is known
143         if (rollershutterMoving) {
144             if (logger.isTraceEnabled()) {
145                 logger.trace("handleRollerShutterCommand: rollershutter {} moving, therefore stop", id);
146             }
147             rollershutterPositionStop();
148         }
149
150         // task to be executed once exact position received from Niko Home Control
151         rollershutterTask = () -> {
152             if (logger.isTraceEnabled()) {
153                 logger.trace("handleRollerShutterCommand: rollershutter {} task running", id);
154             }
155
156             int currentValue = state;
157
158             if (command.equals(NHCDOWN)) {
159                 executeRollershutterDown();
160             } else if (command.equals(NHCUP)) {
161                 executeRollershutterUp();
162             } else if (command.equals(NHCSTOP)) {
163                 executeRollershutterStop();
164             } else {
165                 int newValue = Integer.parseInt(command);
166                 if (logger.isTraceEnabled()) {
167                     logger.trace("handleRollerShutterCommand: rollershutter {} percent command, current {}, new {}", id,
168                             currentValue, newValue);
169                 }
170                 if (currentValue == newValue) {
171                     return;
172                 }
173                 if ((newValue > 0) && (newValue < 100)) {
174                     scheduleRollershutterStop(currentValue, newValue);
175                 }
176                 if (newValue < currentValue) {
177                     executeRollershutterUp();
178                 } else if (newValue > currentValue) {
179                     executeRollershutterDown();
180                 }
181             }
182         };
183
184         // execute immediately if not waiting for exact position
185         if (!waitForEvent) {
186             if (logger.isTraceEnabled()) {
187                 logger.trace("handleRollerShutterCommand: rollershutter {} task executing immediately", id);
188             }
189             executeRollershutterTask();
190         }
191     }
192
193     private void executeRollershutterStop() {
194         nhcComm.executeAction(id, "253");
195     }
196
197     private void executeRollershutterDown() {
198         nhcComm.executeAction(id, "254");
199     }
200
201     private void executeRollershutterUp() {
202         nhcComm.executeAction(id, "255");
203     }
204
205     /**
206      * Method used to stop rollershutter when moving. This will then result in an exact position to be received, so next
207      * percentage movements could be done accurately.
208      */
209     private void rollershutterPositionStop() {
210         if (logger.isTraceEnabled()) {
211             logger.trace("rollershutterPositionStop: rollershutter {} executing", id);
212         }
213         cancelRollershutterStop();
214         rollershutterTask = null;
215         filterEvent = false;
216         waitForEvent = true;
217         executeRollershutterStop();
218     }
219
220     private void executeRollershutterTask() {
221         if (logger.isTraceEnabled()) {
222             logger.trace("executeRollershutterTask: rollershutter {} task triggered", id);
223         }
224         waitForEvent = false;
225
226         Action action = rollershutterTask;
227         if (action != null) {
228             action.execute();
229             rollershutterTask = null;
230         }
231     }
232
233     /**
234      * Method used to schedule a rollershutter stop when moving. This allows stopping the rollershutter at a percent
235      * position.
236      *
237      * @param currentValue current percent position
238      * @param newValue new percent position
239      *
240      */
241     private void scheduleRollershutterStop(int currentValue, int newValue) {
242         // filter first event for a rollershutter coming from Niko Home Control if moving to an intermediate
243         // position to avoid updating state to full open or full close
244         filterEvent = true;
245
246         long duration = rollershutterMoveTime(currentValue, newValue);
247         setRollershutterMovingTrue(duration);
248
249         if (logger.isTraceEnabled()) {
250             logger.trace("scheduleRollershutterStop: schedule rollershutter {} stop in {}ms", id, duration);
251         }
252         rollershutterStopTask = scheduler.schedule(() -> {
253             logger.trace("scheduleRollershutterStop: run rollershutter {} stop", id);
254             executeRollershutterStop();
255         }, duration, TimeUnit.MILLISECONDS);
256     }
257
258     private void cancelRollershutterStop() {
259         ScheduledFuture<?> stopTask = rollershutterStopTask;
260         if (stopTask != null) {
261             if (logger.isTraceEnabled()) {
262                 logger.trace("cancelRollershutterStop: cancel rollershutter {} stop", id);
263             }
264             stopTask.cancel(true);
265         }
266         rollershutterStopTask = null;
267
268         filterEvent = false;
269     }
270
271     private void setRollershutterMovingTrue(long duration) {
272         if (logger.isTraceEnabled()) {
273             logger.trace("setRollershutterMovingTrue: rollershutter {} moving", id);
274         }
275         rollershutterMoving = true;
276         rollershutterMovingFlagTask = scheduler.schedule(() -> {
277             if (logger.isTraceEnabled()) {
278                 logger.trace("setRollershutterMovingTrue: rollershutter {} stopped moving", id);
279             }
280             rollershutterMoving = false;
281         }, duration, TimeUnit.MILLISECONDS);
282     }
283
284     private void setRollershutterMovingFalse() {
285         if (logger.isTraceEnabled()) {
286             logger.trace("setRollershutterMovingFalse: rollershutter {} not moving", id);
287         }
288         rollershutterMoving = false;
289         ScheduledFuture<?> future = rollershutterMovingFlagTask;
290         if (future != null) {
291             future.cancel(true);
292             rollershutterMovingFlagTask = null;
293         }
294     }
295
296     private long rollershutterMoveTime(int currentValue, int newValue) {
297         int totalTime = (newValue > currentValue) ? getOpenTime() : getCloseTime();
298         long duration = Math.abs(newValue - currentValue) * totalTime * 10;
299         if (logger.isTraceEnabled()) {
300             logger.trace("rollershutterMoveTime: rollershutter {} move time {}", id, duration);
301         }
302         return duration;
303     }
304 }