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.homeconnect.internal.handler;
15 import static org.openhab.binding.homeconnect.internal.HomeConnectBindingConstants.*;
16 import static org.openhab.core.library.unit.Units.SECOND;
18 import java.util.Arrays;
19 import java.util.List;
21 import java.util.Optional;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
25 import javax.measure.IncommensurableException;
26 import javax.measure.UnconvertibleException;
27 import javax.measure.quantity.Time;
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.openhab.binding.homeconnect.internal.client.HomeConnectApiClient;
32 import org.openhab.binding.homeconnect.internal.client.exception.ApplianceOfflineException;
33 import org.openhab.binding.homeconnect.internal.client.exception.AuthorizationException;
34 import org.openhab.binding.homeconnect.internal.client.exception.CommunicationException;
35 import org.openhab.binding.homeconnect.internal.client.model.Data;
36 import org.openhab.binding.homeconnect.internal.type.HomeConnectDynamicStateDescriptionProvider;
37 import org.openhab.core.library.types.OnOffType;
38 import org.openhab.core.library.types.QuantityType;
39 import org.openhab.core.thing.Channel;
40 import org.openhab.core.thing.ChannelUID;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.types.Command;
43 import org.openhab.core.types.UnDefType;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link HomeConnectOvenHandler} is responsible for handling commands, which are
49 * sent to one of the channels of a oven.
51 * @author Jonas BrĂ¼stel - Initial contribution
54 public class HomeConnectOvenHandler extends AbstractHomeConnectThingHandler {
56 private static final List<String> INACTIVE_STATE = Arrays.asList(OPERATION_STATE_INACTIVE, OPERATION_STATE_READY);
57 private static final int CAVITY_TEMPERATURE_SCHEDULER_INITIAL_DELAY = 30;
58 private static final int CAVITY_TEMPERATURE_SCHEDULER_PERIOD = 90;
60 private final Logger logger = LoggerFactory.getLogger(HomeConnectOvenHandler.class);
62 private @Nullable ScheduledFuture<?> cavityTemperatureFuture;
63 private boolean manuallyUpdateCavityTemperature;
65 public HomeConnectOvenHandler(Thing thing,
66 HomeConnectDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
67 super(thing, dynamicStateDescriptionProvider);
68 manuallyUpdateCavityTemperature = true;
72 protected void configureChannelUpdateHandlers(Map<String, ChannelUpdateHandler> handlers) {
73 // register default update handlers
74 handlers.put(CHANNEL_OPERATION_STATE, defaultOperationStateChannelUpdateHandler());
75 handlers.put(CHANNEL_POWER_STATE, defaultPowerStateChannelUpdateHandler());
76 handlers.put(CHANNEL_DOOR_STATE, defaultDoorStateChannelUpdateHandler());
77 handlers.put(CHANNEL_REMOTE_CONTROL_ACTIVE_STATE, defaultRemoteControlActiveStateChannelUpdateHandler());
78 handlers.put(CHANNEL_REMOTE_START_ALLOWANCE_STATE, defaultRemoteStartAllowanceChannelUpdateHandler());
79 handlers.put(CHANNEL_SELECTED_PROGRAM_STATE, defaultSelectedProgramStateUpdateHandler());
80 handlers.put(CHANNEL_ACTIVE_PROGRAM_STATE, defaultActiveProgramStateUpdateHandler());
82 // register oven specific update handlers
83 handlers.put(CHANNEL_OVEN_CURRENT_CAVITY_TEMPERATURE,
84 (channelUID, cache) -> updateState(channelUID, cache.putIfAbsentAndGet(channelUID, () -> {
85 Optional<HomeConnectApiClient> apiClient = getApiClient();
86 if (apiClient.isPresent()) {
87 Data data = apiClient.get().getCurrentCavityTemperature(getThingHaId());
88 return new QuantityType<>(data.getValueAsInt(), mapTemperature(data.getUnit()));
90 return UnDefType.UNDEF;
92 handlers.put(CHANNEL_SETPOINT_TEMPERATURE, (channelUID, cache) -> {
93 Optional<Channel> channel = getThingChannel(CHANNEL_SELECTED_PROGRAM_STATE);
94 if (channel.isPresent()) {
95 defaultSelectedProgramStateUpdateHandler().handle(channel.get().getUID(), cache);
98 handlers.put(CHANNEL_DURATION, (channelUID, cache) -> {
99 Optional<Channel> channel = getThingChannel(CHANNEL_SELECTED_PROGRAM_STATE);
100 if (channel.isPresent()) {
101 defaultSelectedProgramStateUpdateHandler().handle(channel.get().getUID(), cache);
107 protected void configureEventHandlers(Map<String, EventHandler> handlers) {
108 // register default SSE event handlers
109 handlers.put(EVENT_DOOR_STATE, defaultDoorStateEventHandler());
110 handlers.put(EVENT_REMOTE_CONTROL_ACTIVE, defaultBooleanEventHandler(CHANNEL_REMOTE_CONTROL_ACTIVE_STATE));
111 handlers.put(EVENT_REMOTE_CONTROL_START_ALLOWED,
112 defaultBooleanEventHandler(CHANNEL_REMOTE_START_ALLOWANCE_STATE));
113 handlers.put(EVENT_SELECTED_PROGRAM, defaultSelectedProgramStateEventHandler());
114 handlers.put(EVENT_FINISH_IN_RELATIVE, defaultRemainingProgramTimeEventHandler());
115 handlers.put(EVENT_REMAINING_PROGRAM_TIME, defaultRemainingProgramTimeEventHandler());
116 handlers.put(EVENT_PROGRAM_PROGRESS, defaultPercentQuantityTypeEventHandler(CHANNEL_PROGRAM_PROGRESS_STATE));
117 handlers.put(EVENT_ELAPSED_PROGRAM_TIME, defaultElapsedProgramTimeEventHandler());
118 handlers.put(EVENT_ACTIVE_PROGRAM, defaultActiveProgramEventHandler());
120 // register oven specific SSE event handlers
121 handlers.put(EVENT_OPERATION_STATE, event -> {
122 defaultOperationStateEventHandler().handle(event);
123 if (STATE_OPERATION_RUN.equals(event.getValue())) {
124 manuallyUpdateCavityTemperature = true;
127 handlers.put(EVENT_POWER_STATE, event -> {
128 getThingChannel(CHANNEL_POWER_STATE).ifPresent(
129 channel -> updateState(channel.getUID(), OnOffType.from(STATE_POWER_ON.equals(event.getValue()))));
131 if (STATE_POWER_ON.equals(event.getValue())) {
134 resetProgramStateChannels(true);
135 getThingChannel(CHANNEL_SELECTED_PROGRAM_STATE)
136 .ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
137 getThingChannel(CHANNEL_ACTIVE_PROGRAM_STATE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
138 getThingChannel(CHANNEL_SETPOINT_TEMPERATURE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
139 getThingChannel(CHANNEL_DURATION).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
143 handlers.put(EVENT_OVEN_CAVITY_TEMPERATURE, event -> {
144 manuallyUpdateCavityTemperature = false;
145 getThingChannel(CHANNEL_OVEN_CURRENT_CAVITY_TEMPERATURE).ifPresent(channel -> updateState(channel.getUID(),
146 new QuantityType<>(event.getValueAsInt(), mapTemperature(event.getUnit()))));
149 handlers.put(EVENT_SETPOINT_TEMPERATURE,
150 event -> getThingChannel(CHANNEL_SETPOINT_TEMPERATURE)
151 .ifPresent(channel -> updateState(channel.getUID(),
152 new QuantityType<>(event.getValueAsInt(), mapTemperature(event.getUnit())))));
153 handlers.put(EVENT_DURATION, event -> getThingChannel(CHANNEL_DURATION).ifPresent(
154 channel -> updateState(channel.getUID(), new QuantityType<>(event.getValueAsInt(), SECOND))));
158 protected void handleCommand(final ChannelUID channelUID, final Command command,
159 final HomeConnectApiClient apiClient)
160 throws CommunicationException, AuthorizationException, ApplianceOfflineException {
161 super.handleCommand(channelUID, command, apiClient);
163 handlePowerCommand(channelUID, command, apiClient, STATE_POWER_STANDBY);
165 String operationState = getOperationState();
166 if (operationState != null && INACTIVE_STATE.contains(operationState) && command instanceof QuantityType) {
167 // set setpoint temperature
168 if (CHANNEL_SETPOINT_TEMPERATURE.equals(channelUID.getId())) {
169 handleTemperatureCommand(channelUID, command, apiClient);
170 } else if (CHANNEL_DURATION.equals(channelUID.getId())) {
171 @SuppressWarnings("unchecked")
172 QuantityType<Time> quantity = ((QuantityType<Time>) command);
175 String value = String
176 .valueOf(quantity.getUnit().getConverterToAny(SECOND).convert(quantity).intValue());
177 logger.debug("Set duration to {} seconds. haId={}", value, getThingHaId());
179 apiClient.setProgramOptions(getThingHaId(), OPTION_DURATION, value, "seconds", true, false);
180 } catch (IncommensurableException | UnconvertibleException e) {
181 logger.warn("Could not set duration! haId={}, error={}", getThingHaId(), e.getMessage());
185 logger.debug("Device can not handle command {} in current operation state ({}). haId={}", command,
186 operationState, getThingHaId());
191 public void initialize() {
193 cavityTemperatureFuture = scheduler.scheduleWithFixedDelay(() -> {
194 String operationState = getOperationState();
195 boolean manuallyUpdateCavityTemperature = this.manuallyUpdateCavityTemperature;
197 if (STATE_OPERATION_RUN.equals(operationState)) {
198 getThingChannel(CHANNEL_OVEN_CURRENT_CAVITY_TEMPERATURE).ifPresent(c -> {
199 if (manuallyUpdateCavityTemperature) {
200 logger.debug("Update cavity temperature manually via API. haId={}", getThingHaId());
201 updateChannel(c.getUID());
203 logger.debug("Update cavity temperature via SSE, don't need to fetch manually. haId={}",
208 }, CAVITY_TEMPERATURE_SCHEDULER_INITIAL_DELAY, CAVITY_TEMPERATURE_SCHEDULER_PERIOD, TimeUnit.SECONDS);
212 public void dispose() {
213 ScheduledFuture<?> cavityTemperatureFuture = this.cavityTemperatureFuture;
214 if (cavityTemperatureFuture != null) {
215 cavityTemperatureFuture.cancel(true);
221 public String toString() {
222 return "HomeConnectOvenHandler [haId: " + getThingHaId() + "]";
226 protected void resetProgramStateChannels(boolean offline) {
227 super.resetProgramStateChannels(offline);
228 getThingChannel(CHANNEL_REMAINING_PROGRAM_TIME_STATE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
229 getThingChannel(CHANNEL_PROGRAM_PROGRESS_STATE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
230 getThingChannel(CHANNEL_ELAPSED_PROGRAM_TIME).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));