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 @SuppressWarnings("PMD.SimplifyBooleanReturns")
59 private boolean hasFinishedChangedFromPreviousState(DeviceState previous) {
60 if (previous.getStateType().equals(nextState.getStateType())) {
64 if (isInRunningState(previous) && nextState.isInState(StateType.FAILURE)) {
68 if (isInRunningState(previous) != isInRunningState(nextState)) {
72 if (nextState.isInState(StateType.OFF)) {
80 * Gets whether a program finished.
82 * @return Whether a program finished.
84 public Optional<Boolean> isFinished() {
85 return previousState.flatMap(this::hasFinishedFromPreviousState);
88 private Optional<Boolean> hasFinishedFromPreviousState(DeviceState prevState) {
89 if (prevState.getStateType().isEmpty()) {
90 return Optional.empty();
93 if (nextState.isInState(StateType.OFF)) {
94 return Optional.of(false);
97 if (nextState.isInState(StateType.FAILURE)) {
98 return Optional.of(false);
101 return Optional.of(!isInRunningState(nextState));
105 * Gets the remaining time of the active program.
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.
112 * @return The remaining time in seconds.
114 public Optional<Integer> getRemainingTime() {
115 if (!remainingTimeWasSetInCurrentProgram && isInRunningState(nextState)) {
116 return nextState.getRemainingTime().filter(it -> it != 0);
118 return nextState.getRemainingTime();
123 * Gets the program progress.
125 * @return The progress of the active program in percent.
127 public Optional<Integer> getProgress() {
128 if (getRemainingTime().isPresent()) {
129 return nextState.getProgress();
131 return Optional.empty();
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();
145 private static boolean isInRunningState(DeviceState device) {
146 return device.isInState(StateType.RUNNING) || device.isInState(StateType.PAUSE);