2 * Copyright (c) 2010-2021 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.handler;
15 import static org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.Channels.*;
16 import static org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus.*;
17 import static org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus.*;
18 import static org.openhab.binding.mielecloud.internal.webservice.api.json.ProcessAction.*;
20 import java.util.Optional;
21 import java.util.function.Consumer;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants;
25 import org.openhab.binding.mielecloud.internal.MieleCloudBindingConstants.I18NKeys;
26 import org.openhab.binding.mielecloud.internal.discovery.ThingInformationExtractor;
27 import org.openhab.binding.mielecloud.internal.handler.channel.ActionsChannelState;
28 import org.openhab.binding.mielecloud.internal.handler.channel.DeviceChannelState;
29 import org.openhab.binding.mielecloud.internal.handler.channel.TransitionChannelState;
30 import org.openhab.binding.mielecloud.internal.webservice.ActionStateFetcher;
31 import org.openhab.binding.mielecloud.internal.webservice.MieleWebservice;
32 import org.openhab.binding.mielecloud.internal.webservice.UnavailableMieleWebservice;
33 import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState;
34 import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState;
35 import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus;
36 import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus;
37 import org.openhab.binding.mielecloud.internal.webservice.api.TransitionState;
38 import org.openhab.binding.mielecloud.internal.webservice.api.json.ProcessAction;
39 import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType;
40 import org.openhab.binding.mielecloud.internal.webservice.exception.TooManyRequestsException;
41 import org.openhab.core.library.types.OnOffType;
42 import org.openhab.core.thing.Bridge;
43 import org.openhab.core.thing.ChannelUID;
44 import org.openhab.core.thing.Thing;
45 import org.openhab.core.thing.ThingStatus;
46 import org.openhab.core.thing.ThingStatusDetail;
47 import org.openhab.core.thing.binding.BaseThingHandler;
48 import org.openhab.core.thing.binding.BridgeHandler;
49 import org.openhab.core.types.Command;
50 import org.openhab.core.types.RefreshType;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 * Abstract base class for all Miele thing handlers.
57 * @author Roland Edelhoff - Initial contribution
58 * @author Björn Lange - Add channel state wrappers
61 public abstract class AbstractMieleThingHandler extends BaseThingHandler {
62 protected final ActionStateFetcher actionFetcher;
63 protected DeviceState latestDeviceState = new DeviceState(getDeviceId(), null);
64 protected TransitionState latestTransitionState = new TransitionState(null, latestDeviceState);
65 protected ActionsState latestActionsState = new ActionsState(getDeviceId(), null);
67 private final Logger logger = LoggerFactory.getLogger(this.getClass());
70 * Creates a new {@link AbstractMieleThingHandler}.
72 * @param thing The thing to handle.
74 public AbstractMieleThingHandler(Thing thing) {
76 this.actionFetcher = new ActionStateFetcher(this::getWebservice, scheduler);
79 private Optional<MieleBridgeHandler> getMieleBridgeHandler() {
80 Bridge bridge = getBridge();
82 return Optional.empty();
85 BridgeHandler handler = bridge.getHandler();
86 if (handler == null || !(handler instanceof MieleBridgeHandler)) {
87 return Optional.empty();
90 return Optional.of((MieleBridgeHandler) handler);
93 protected MieleWebservice getWebservice() {
94 return getMieleBridgeHandler().map(MieleBridgeHandler::getWebservice)
95 .orElse(UnavailableMieleWebservice.INSTANCE);
99 public void initialize() {
100 getWebservice().dispatchDeviceState(getDeviceId());
102 // If no device state update was received so far, set the device to OFFLINE.
103 if (getThing().getStatus() == ThingStatus.INITIALIZING) {
104 updateStatus(ThingStatus.OFFLINE);
109 public void handleCommand(ChannelUID channelUID, Command command) {
110 if (RefreshType.REFRESH.equals(command)) {
111 updateDeviceState(new DeviceChannelState(latestDeviceState));
112 updateTransitionState(new TransitionChannelState(latestTransitionState));
113 updateActionState(new ActionsChannelState(latestActionsState));
116 switch (channelUID.getId()) {
117 case PROGRAM_START_STOP:
118 if (PROGRAM_STARTED.matches(command.toString())) {
119 triggerProcessAction(START);
120 } else if (PROGRAM_STOPPED.matches(command.toString())) {
121 triggerProcessAction(STOP);
125 case PROGRAM_START_STOP_PAUSE:
126 if (PROGRAM_STARTED.matches(command.toString())) {
127 triggerProcessAction(START);
128 } else if (PROGRAM_STOPPED.matches(command.toString())) {
129 triggerProcessAction(STOP);
130 } else if (PROGRAM_PAUSED.matches(command.toString())) {
131 triggerProcessAction(PAUSE);
136 if (command instanceof OnOffType) {
137 triggerLight(OnOffType.ON.equals(command));
142 if (POWER_ON.matches(command.toString()) || POWER_OFF.matches(command.toString())) {
143 triggerPowerState(OnOffType.ON.equals(OnOffType.from(command.toString())));
150 public void dispose() {
154 * Invoked when an update of the available actions for the device managed by this handler is received from the Miele
157 public final void onProcessActionUpdated(ActionsState actionState) {
158 latestActionsState = actionState;
159 updateActionState(new ActionsChannelState(latestActionsState));
163 * Invoked when the device managed by this handler was removed from the Miele cloud.
165 public final void onDeviceRemoved() {
166 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.GONE, I18NKeys.THING_STATUS_DESCRIPTION_REMOVED);
170 * Invoked when a device state update for the device managed by this handler is received from the Miele cloud.
172 public final void onDeviceStateUpdated(DeviceState deviceState) {
173 actionFetcher.onDeviceStateUpdated(deviceState);
175 latestTransitionState = new TransitionState(latestTransitionState, deviceState);
176 latestDeviceState = deviceState;
178 updateThingProperties(deviceState);
179 updateDeviceState(new DeviceChannelState(latestDeviceState));
180 updateTransitionState(new TransitionChannelState(latestTransitionState));
181 updateThingStatus(latestDeviceState);
184 protected void triggerProcessAction(final ProcessAction processAction) {
185 performPutAction(() -> getWebservice().putProcessAction(getDeviceId(), processAction),
186 t -> logger.warn("Failed to perform '{}' operation for device '{}'.", processAction, getDeviceId(), t));
189 protected void triggerLight(final boolean on) {
190 performPutAction(() -> getWebservice().putLight(getDeviceId(), on),
191 t -> logger.warn("Failed to set light state to '{}' for device '{}'.", on, getDeviceId(), t));
194 protected void triggerPowerState(final boolean on) {
195 performPutAction(() -> getWebservice().putPowerState(getDeviceId(), on),
196 t -> logger.warn("Failed to set the power state to '{}' for device '{}'.", on, getDeviceId(), t));
199 protected void triggerProgram(final long programId) {
200 performPutAction(() -> getWebservice().putProgram(getDeviceId(), programId), t -> logger
201 .warn("Failed to activate program with ID '{}' for device '{}'.", programId, getDeviceId(), t));
204 private void performPutAction(Runnable action, Consumer<Exception> onError) {
205 scheduler.execute(() -> {
208 } catch (TooManyRequestsException e) {
209 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
210 I18NKeys.THING_STATUS_DESCRIPTION_RATELIMIT);
212 } catch (Exception e) {
218 protected final String getDeviceId() {
219 return getConfig().get(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER).toString();
223 * Creates a {@link ChannelUID} from the given name.
225 * @param name channel name
226 * @return {@link ChannelUID}
228 protected ChannelUID channel(String name) {
229 return new ChannelUID(getThing().getUID(), name);
233 * Updates the thing status depending on whether the managed device is connected and reachable.
235 private void updateThingStatus(DeviceState deviceState) {
236 if (deviceState.isInState(StateType.NOT_CONNECTED)) {
237 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
238 I18NKeys.THING_STATUS_DESCRIPTION_DISCONNECTED);
240 updateStatus(ThingStatus.ONLINE);
245 * Determines the status of the currently selected program.
247 protected ProgramStatus getProgramStatus(StateType rawStatus) {
248 if (rawStatus.equals(StateType.RUNNING)) {
249 return PROGRAM_STARTED;
251 return PROGRAM_STOPPED;
255 * Determines the power status of the managed device.
257 protected PowerStatus getPowerStatus(StateType rawStatus) {
258 if (rawStatus.equals(StateType.OFF) || rawStatus.equals(StateType.NOT_CONNECTED)) {
265 * Updates the thing properties. This is necessary if properties have not been set during discovery.
267 private void updateThingProperties(DeviceState deviceState) {
268 var properties = editProperties();
269 properties.putAll(ThingInformationExtractor.extractProperties(getThing().getThingTypeUID(), deviceState));
270 updateProperties(properties);
274 * Updates the device state channels.
276 * @param device The {@link DeviceChannelState} information to update the device channel states with.
278 protected abstract void updateDeviceState(DeviceChannelState device);
281 * Updates the transition state channels.
283 * @param transition The {@link TransitionChannelState} information to update the transition channel states with.
285 protected abstract void updateTransitionState(TransitionChannelState transition);
288 * Updates the device action state channels.
290 * @param action The {@link ActionsChannelState} information to update the action channel states with.
292 protected abstract void updateActionState(ActionsChannelState actions);