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_REMAINING_PROGRAM_TIME, defaultRemainingProgramTimeEventHandler());
115 handlers.put(EVENT_PROGRAM_PROGRESS, defaultPercentQuantityTypeEventHandler(CHANNEL_PROGRAM_PROGRESS_STATE));
116 handlers.put(EVENT_ELAPSED_PROGRAM_TIME, defaultElapsedProgramTimeEventHandler());
117 handlers.put(EVENT_ACTIVE_PROGRAM, defaultActiveProgramEventHandler());
119 // register oven specific SSE event handlers
120 handlers.put(EVENT_OPERATION_STATE, event -> {
121 defaultOperationStateEventHandler().handle(event);
122 if (STATE_OPERATION_RUN.equals(event.getValue())) {
123 manuallyUpdateCavityTemperature = true;
126 handlers.put(EVENT_POWER_STATE, event -> {
127 getThingChannel(CHANNEL_POWER_STATE).ifPresent(
128 channel -> updateState(channel.getUID(), OnOffType.from(STATE_POWER_ON.equals(event.getValue()))));
130 if (STATE_POWER_ON.equals(event.getValue())) {
133 resetProgramStateChannels();
134 getThingChannel(CHANNEL_SELECTED_PROGRAM_STATE)
135 .ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
136 getThingChannel(CHANNEL_ACTIVE_PROGRAM_STATE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
137 getThingChannel(CHANNEL_SETPOINT_TEMPERATURE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
138 getThingChannel(CHANNEL_DURATION).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
142 handlers.put(EVENT_OVEN_CAVITY_TEMPERATURE, event -> {
143 manuallyUpdateCavityTemperature = false;
144 getThingChannel(CHANNEL_OVEN_CURRENT_CAVITY_TEMPERATURE).ifPresent(channel -> updateState(channel.getUID(),
145 new QuantityType<>(event.getValueAsInt(), mapTemperature(event.getUnit()))));
148 handlers.put(EVENT_SETPOINT_TEMPERATURE,
149 event -> getThingChannel(CHANNEL_SETPOINT_TEMPERATURE)
150 .ifPresent(channel -> updateState(channel.getUID(),
151 new QuantityType<>(event.getValueAsInt(), mapTemperature(event.getUnit())))));
152 handlers.put(EVENT_DURATION, event -> getThingChannel(CHANNEL_DURATION).ifPresent(
153 channel -> updateState(channel.getUID(), new QuantityType<>(event.getValueAsInt(), SECOND))));
157 protected void handleCommand(final ChannelUID channelUID, final Command command,
158 final HomeConnectApiClient apiClient)
159 throws CommunicationException, AuthorizationException, ApplianceOfflineException {
160 super.handleCommand(channelUID, command, apiClient);
162 // turn coffee maker on and standby
163 if (command instanceof OnOffType && CHANNEL_POWER_STATE.equals(channelUID.getId())) {
164 apiClient.setPowerState(getThingHaId(),
165 OnOffType.ON.equals(command) ? STATE_POWER_ON : STATE_POWER_STANDBY);
168 String operationState = getOperationState();
169 if (operationState != null && INACTIVE_STATE.contains(operationState) && command instanceof QuantityType) {
170 // set setpoint temperature
171 if (CHANNEL_SETPOINT_TEMPERATURE.equals(channelUID.getId())) {
172 handleTemperatureCommand(channelUID, command, apiClient);
173 } else if (CHANNEL_DURATION.equals(channelUID.getId())) {
174 @SuppressWarnings("unchecked")
175 QuantityType<Time> quantity = ((QuantityType<Time>) command);
178 String value = String
179 .valueOf(quantity.getUnit().getConverterToAny(SECOND).convert(quantity).intValue());
180 logger.debug("Set duration to {} seconds. haId={}", value, getThingHaId());
182 apiClient.setProgramOptions(getThingHaId(), OPTION_DURATION, value, "seconds", true, false);
183 } catch (IncommensurableException | UnconvertibleException e) {
184 logger.warn("Could not set duration! haId={}, error={}", getThingHaId(), e.getMessage());
188 logger.debug("Device can not handle command {} in current operation state ({}). haId={}", command,
189 operationState, getThingHaId());
194 public void initialize() {
196 cavityTemperatureFuture = scheduler.scheduleWithFixedDelay(() -> {
197 String operationState = getOperationState();
198 boolean manuallyUpdateCavityTemperature = this.manuallyUpdateCavityTemperature;
200 if (STATE_OPERATION_RUN.equals(operationState)) {
201 getThingChannel(CHANNEL_OVEN_CURRENT_CAVITY_TEMPERATURE).ifPresent(c -> {
202 if (manuallyUpdateCavityTemperature) {
203 logger.debug("Update cavity temperature manually via API. haId={}", getThingHaId());
204 updateChannel(c.getUID());
206 logger.debug("Update cavity temperature via SSE, don't need to fetch manually. haId={}",
211 }, CAVITY_TEMPERATURE_SCHEDULER_INITIAL_DELAY, CAVITY_TEMPERATURE_SCHEDULER_PERIOD, TimeUnit.SECONDS);
215 public void dispose() {
216 ScheduledFuture<?> cavityTemperatureFuture = this.cavityTemperatureFuture;
217 if (cavityTemperatureFuture != null) {
218 cavityTemperatureFuture.cancel(true);
224 public String toString() {
225 return "HomeConnectOvenHandler [haId: " + getThingHaId() + "]";
229 protected void resetProgramStateChannels() {
230 super.resetProgramStateChannels();
231 getThingChannel(CHANNEL_REMAINING_PROGRAM_TIME_STATE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
232 getThingChannel(CHANNEL_PROGRAM_PROGRESS_STATE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
233 getThingChannel(CHANNEL_ELAPSED_PROGRAM_TIME).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));