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.mielecloud.internal.webservice.api;
15 import java.util.Optional;
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;
22 * This immutable class provides methods to extract the state information related to state transitions in a comfortable
25 * @author Björn Lange - Initial contribution
28 public class TransitionState {
29 private final boolean remainingTimeWasSetInCurrentProgram;
30 private final Optional<DeviceState> previousState;
31 private final DeviceState nextState;
34 * Creates a new {@link TransitionState}.
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.
39 * @param previousTransitionState The previous transition state if it exists.
40 * @param nextState The device state which the device is transitioning to.
42 public TransitionState(@Nullable TransitionState previousTransitionState, DeviceState nextState) {
43 this.remainingTimeWasSetInCurrentProgram = wasRemainingTimeSetInCurrentProgram(previousTransitionState,
45 this.previousState = Optional.ofNullable(previousTransitionState).map(it -> it.nextState);
46 this.nextState = nextState;
50 * Gets whether the finish state changed due to the transition form the previous to the current state.
52 * @return Whether the finish state changed due to the transition form the previous to the current state.
54 public boolean hasFinishedChanged() {
55 return previousState.map(this::hasFinishedChangedFromPreviousState).orElse(true);
58 private boolean hasFinishedChangedFromPreviousState(DeviceState previous) {
59 if (previous.getStateType().equals(nextState.getStateType())) {
63 if (isInRunningState(previous) && nextState.isInState(StateType.FAILURE)) {
67 if (isInRunningState(previous) != isInRunningState(nextState)) {
71 if (nextState.isInState(StateType.OFF)) {
79 * Gets whether a program finished.
81 * @return Whether a program finished.
83 public Optional<Boolean> isFinished() {
84 return previousState.flatMap(this::hasFinishedFromPreviousState);
87 private Optional<Boolean> hasFinishedFromPreviousState(DeviceState prevState) {
88 if (!prevState.getStateType().isPresent()) {
89 return Optional.empty();
92 if (nextState.isInState(StateType.OFF)) {
93 return Optional.of(false);
96 if (nextState.isInState(StateType.FAILURE)) {
97 return Optional.of(false);
100 return Optional.of(!isInRunningState(nextState));
104 * Gets the remaining time of the active program.
106 * Note: Tracking changes in the remaining time is a workaround for the Miele API not properly distinguishing
107 * between "there is no remaining time set" and "the remaining time is zero". If the remaining time is zero when a
108 * program is started then we assume that no timer was set / program with remaining time is active. This may be
109 * changed later by the user which is detected by the remaining time changing from 0 to some larger value.
111 * @return The remaining time in seconds.
113 public Optional<Integer> getRemainingTime() {
114 if (!remainingTimeWasSetInCurrentProgram && isInRunningState(nextState)) {
115 return nextState.getRemainingTime().filter(it -> it != 0);
117 return nextState.getRemainingTime();
122 * Gets the program progress.
124 * @return The progress of the active program in percent.
126 public Optional<Integer> getProgress() {
127 if (getRemainingTime().isPresent()) {
128 return nextState.getProgress();
130 return Optional.empty();
134 private static boolean wasRemainingTimeSetInCurrentProgram(@Nullable TransitionState previousTransitionState,
135 DeviceState nextState) {
136 if (previousTransitionState != null && isInRunningState(previousTransitionState.nextState)) {
137 return previousTransitionState.remainingTimeWasSetInCurrentProgram
138 || previousTransitionState.getRemainingTime().isPresent();
144 private static boolean isInRunningState(DeviceState device) {
145 return device.isInState(StateType.RUNNING) || device.isInState(StateType.PAUSE);