]> git.basschouten.com Git - openhab-addons.git/blob
4a4cccda6a2910acd0e0d4cb0a14b92e6a32d8fb
[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.mielecloud.internal.webservice.api;
14
15 import java.util.Optional;
16
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
20
21 /**
22  * This immutable class provides methods to extract the state information related to state transitions in a comfortable
23  * way.
24  *
25  * @author Björn Lange - Initial contribution
26  */
27 @NonNullByDefault
28 public class TransitionState {
29     private final boolean remainingTimeWasSetInCurrentProgram;
30     private final Optional<DeviceState> previousState;
31     private final DeviceState nextState;
32
33     /**
34      * Creates a new {@link TransitionState}.
35      *
36      * Note: {@code previousState} <b>must not</b> be saved in a field in this class as this will create a linked list
37      * and cause memory issues. The constructor only serves the purpose of unpacking state that must be carried on.
38      *
39      * @param previousTransitionState The previous transition state if it exists.
40      * @param nextState The device state which the device is transitioning to.
41      */
42     public TransitionState(@Nullable TransitionState previousTransitionState, DeviceState nextState) {
43         this.remainingTimeWasSetInCurrentProgram = wasRemainingTimeSetInCurrentProgram(previousTransitionState,
44                 nextState);
45         this.previousState = Optional.ofNullable(previousTransitionState).map(it -> it.nextState);
46         this.nextState = nextState;
47     }
48
49     /**
50      * Gets whether the finish state changed due to the transition form the previous to the current state.
51      *
52      * @return Whether the finish state changed due to the transition form the previous to the current state.
53      */
54     public boolean hasFinishedChanged() {
55         return previousState.map(this::hasFinishedChangedFromPreviousState).orElse(true);
56     }
57
58     @SuppressWarnings("PMD.SimplifyBooleanReturns")
59     private boolean hasFinishedChangedFromPreviousState(DeviceState previous) {
60         if (previous.getStateType().equals(nextState.getStateType())) {
61             return false;
62         }
63
64         if (isInRunningState(previous) && nextState.isInState(StateType.FAILURE)) {
65             return false;
66         }
67
68         if (isInRunningState(previous) != isInRunningState(nextState)) {
69             return true;
70         }
71
72         if (nextState.isInState(StateType.OFF)) {
73             return true;
74         }
75
76         return false;
77     }
78
79     /**
80      * Gets whether a program finished.
81      *
82      * @return Whether a program finished.
83      */
84     public Optional<Boolean> isFinished() {
85         return previousState.flatMap(this::hasFinishedFromPreviousState);
86     }
87
88     private Optional<Boolean> hasFinishedFromPreviousState(DeviceState prevState) {
89         if (prevState.getStateType().isEmpty()) {
90             return Optional.empty();
91         }
92
93         if (nextState.isInState(StateType.OFF)) {
94             return Optional.of(false);
95         }
96
97         if (nextState.isInState(StateType.FAILURE)) {
98             return Optional.of(false);
99         }
100
101         return Optional.of(!isInRunningState(nextState));
102     }
103
104     /**
105      * Gets the remaining time of the active program.
106      *
107      * Note: Tracking changes in the remaining time is a workaround for the Miele API not properly distinguishing
108      * between "there is no remaining time set" and "the remaining time is zero". If the remaining time is zero when a
109      * program is started then we assume that no timer was set / program with remaining time is active. This may be
110      * changed later by the user which is detected by the remaining time changing from 0 to some larger value.
111      *
112      * @return The remaining time in seconds.
113      */
114     public Optional<Integer> getRemainingTime() {
115         if (!remainingTimeWasSetInCurrentProgram && isInRunningState(nextState)) {
116             return nextState.getRemainingTime().filter(it -> it != 0);
117         } else {
118             return nextState.getRemainingTime();
119         }
120     }
121
122     /**
123      * Gets the program progress.
124      *
125      * @return The progress of the active program in percent.
126      */
127     public Optional<Integer> getProgress() {
128         if (getRemainingTime().isPresent()) {
129             return nextState.getProgress();
130         } else {
131             return Optional.empty();
132         }
133     }
134
135     private static boolean wasRemainingTimeSetInCurrentProgram(@Nullable TransitionState previousTransitionState,
136             DeviceState nextState) {
137         if (previousTransitionState != null && isInRunningState(previousTransitionState.nextState)) {
138             return previousTransitionState.remainingTimeWasSetInCurrentProgram
139                     || previousTransitionState.getRemainingTime().isPresent();
140         } else {
141             return false;
142         }
143     }
144
145     private static boolean isInRunningState(DeviceState device) {
146         return device.isInState(StateType.RUNNING) || device.isInState(StateType.PAUSE);
147     }
148 }