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.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.ChannelUID;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.types.Command;
42 import org.openhab.core.types.UnDefType;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * The {@link HomeConnectOvenHandler} is responsible for handling commands, which are
48 * sent to one of the channels of an oven.
50 * @author Jonas BrĂ¼stel - Initial contribution
53 public class HomeConnectOvenHandler extends AbstractHomeConnectThingHandler {
55 private static final List<String> INACTIVE_STATE = Arrays.asList(OPERATION_STATE_INACTIVE, OPERATION_STATE_READY);
56 private static final int CAVITY_TEMPERATURE_SCHEDULER_INITIAL_DELAY = 30;
57 private static final int CAVITY_TEMPERATURE_SCHEDULER_PERIOD = 90;
59 private final Logger logger = LoggerFactory.getLogger(HomeConnectOvenHandler.class);
61 private @Nullable ScheduledFuture<?> cavityTemperatureFuture;
62 private boolean manuallyUpdateCavityTemperature;
64 public HomeConnectOvenHandler(Thing thing,
65 HomeConnectDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
66 super(thing, dynamicStateDescriptionProvider);
67 manuallyUpdateCavityTemperature = true;
71 protected void configureChannelUpdateHandlers(Map<String, ChannelUpdateHandler> handlers) {
72 // register default update handlers
73 handlers.put(CHANNEL_OPERATION_STATE, defaultOperationStateChannelUpdateHandler());
74 handlers.put(CHANNEL_POWER_STATE, defaultPowerStateChannelUpdateHandler());
75 handlers.put(CHANNEL_DOOR_STATE, defaultDoorStateChannelUpdateHandler());
76 handlers.put(CHANNEL_REMOTE_CONTROL_ACTIVE_STATE, defaultRemoteControlActiveStateChannelUpdateHandler());
77 handlers.put(CHANNEL_REMOTE_START_ALLOWANCE_STATE, defaultRemoteStartAllowanceChannelUpdateHandler());
78 handlers.put(CHANNEL_SELECTED_PROGRAM_STATE, defaultSelectedProgramStateUpdateHandler());
79 handlers.put(CHANNEL_ACTIVE_PROGRAM_STATE, defaultActiveProgramStateUpdateHandler());
81 // register oven specific update handlers
82 handlers.put(CHANNEL_OVEN_CURRENT_CAVITY_TEMPERATURE,
83 (channelUID, cache) -> updateState(channelUID, cache.putIfAbsentAndGet(channelUID, () -> {
84 Optional<HomeConnectApiClient> apiClient = getApiClient();
85 if (apiClient.isPresent()) {
86 Data data = apiClient.get().getCurrentCavityTemperature(getThingHaId());
87 return new QuantityType<>(data.getValueAsInt(), mapTemperature(data.getUnit()));
89 return UnDefType.UNDEF;
91 handlers.put(CHANNEL_SETPOINT_TEMPERATURE, getAndUpdateSelectedProgramStateUpdateHandler());
92 handlers.put(CHANNEL_DURATION, getAndUpdateSelectedProgramStateUpdateHandler());
96 protected void configureEventHandlers(Map<String, EventHandler> handlers) {
97 // register default SSE event handlers
98 handlers.put(EVENT_DOOR_STATE, defaultDoorStateEventHandler());
99 handlers.put(EVENT_REMOTE_CONTROL_ACTIVE, defaultBooleanEventHandler(CHANNEL_REMOTE_CONTROL_ACTIVE_STATE));
100 handlers.put(EVENT_REMOTE_CONTROL_START_ALLOWED,
101 defaultBooleanEventHandler(CHANNEL_REMOTE_START_ALLOWANCE_STATE));
102 handlers.put(EVENT_SELECTED_PROGRAM, defaultSelectedProgramStateEventHandler());
103 handlers.put(EVENT_FINISH_IN_RELATIVE, defaultRemainingProgramTimeEventHandler());
104 handlers.put(EVENT_REMAINING_PROGRAM_TIME, defaultRemainingProgramTimeEventHandler());
105 handlers.put(EVENT_PROGRAM_PROGRESS, defaultPercentQuantityTypeEventHandler(CHANNEL_PROGRAM_PROGRESS_STATE));
106 handlers.put(EVENT_ELAPSED_PROGRAM_TIME, defaultElapsedProgramTimeEventHandler());
107 handlers.put(EVENT_ACTIVE_PROGRAM, defaultActiveProgramEventHandler());
109 // register oven specific SSE event handlers
110 handlers.put(EVENT_OPERATION_STATE, event -> {
111 defaultOperationStateEventHandler().handle(event);
112 if (STATE_OPERATION_RUN.equals(event.getValue())) {
113 manuallyUpdateCavityTemperature = true;
116 handlers.put(EVENT_POWER_STATE, event -> {
117 getLinkedChannel(CHANNEL_POWER_STATE).ifPresent(
118 channel -> updateState(channel.getUID(), OnOffType.from(STATE_POWER_ON.equals(event.getValue()))));
120 if (STATE_POWER_ON.equals(event.getValue())) {
123 resetProgramStateChannels(true);
124 getLinkedChannel(CHANNEL_SELECTED_PROGRAM_STATE)
125 .ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
126 getLinkedChannel(CHANNEL_ACTIVE_PROGRAM_STATE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
127 getLinkedChannel(CHANNEL_SETPOINT_TEMPERATURE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
128 getLinkedChannel(CHANNEL_DURATION).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
132 handlers.put(EVENT_OVEN_CAVITY_TEMPERATURE, event -> {
133 manuallyUpdateCavityTemperature = false;
134 getLinkedChannel(CHANNEL_OVEN_CURRENT_CAVITY_TEMPERATURE).ifPresent(channel -> updateState(channel.getUID(),
135 new QuantityType<>(event.getValueAsInt(), mapTemperature(event.getUnit()))));
138 handlers.put(EVENT_SETPOINT_TEMPERATURE,
139 event -> getLinkedChannel(CHANNEL_SETPOINT_TEMPERATURE)
140 .ifPresent(channel -> updateState(channel.getUID(),
141 new QuantityType<>(event.getValueAsInt(), mapTemperature(event.getUnit())))));
142 handlers.put(EVENT_DURATION, event -> getLinkedChannel(CHANNEL_DURATION).ifPresent(
143 channel -> updateState(channel.getUID(), new QuantityType<>(event.getValueAsInt(), SECOND))));
147 protected void handleCommand(final ChannelUID channelUID, final Command command,
148 final HomeConnectApiClient apiClient)
149 throws CommunicationException, AuthorizationException, ApplianceOfflineException {
150 super.handleCommand(channelUID, command, apiClient);
152 handlePowerCommand(channelUID, command, apiClient, STATE_POWER_STANDBY);
154 String operationState = getOperationState();
155 if (operationState != null && INACTIVE_STATE.contains(operationState) && command instanceof QuantityType) {
156 // set setpoint temperature
157 if (CHANNEL_SETPOINT_TEMPERATURE.equals(channelUID.getId())) {
158 handleTemperatureCommand(channelUID, command, apiClient);
159 } else if (CHANNEL_DURATION.equals(channelUID.getId())) {
160 @SuppressWarnings("unchecked")
161 QuantityType<Time> quantity = ((QuantityType<Time>) command);
164 String value = String
165 .valueOf(quantity.getUnit().getConverterToAny(SECOND).convert(quantity).intValue());
166 logger.debug("Set duration to {} seconds. haId={}", value, getThingHaId());
168 apiClient.setProgramOptions(getThingHaId(), OPTION_DURATION, value, "seconds", true, false);
169 } catch (IncommensurableException | UnconvertibleException e) {
170 logger.warn("Could not set duration! haId={}, error={}", getThingHaId(), e.getMessage());
174 logger.debug("Device can not handle command {} in current operation state ({}). haId={}", command,
175 operationState, getThingHaId());
180 public void initialize() {
182 cavityTemperatureFuture = scheduler.scheduleWithFixedDelay(() -> {
183 String operationState = getOperationState();
184 boolean manuallyUpdateCavityTemperature = this.manuallyUpdateCavityTemperature;
186 if (STATE_OPERATION_RUN.equals(operationState)) {
187 getThingChannel(CHANNEL_OVEN_CURRENT_CAVITY_TEMPERATURE).ifPresent(c -> {
188 if (manuallyUpdateCavityTemperature) {
189 logger.debug("Update cavity temperature manually via API. haId={}", getThingHaId());
190 updateChannel(c.getUID());
192 logger.debug("Update cavity temperature via SSE, don't need to fetch manually. haId={}",
197 }, CAVITY_TEMPERATURE_SCHEDULER_INITIAL_DELAY, CAVITY_TEMPERATURE_SCHEDULER_PERIOD, TimeUnit.SECONDS);
201 public void dispose() {
202 ScheduledFuture<?> cavityTemperatureFuture = this.cavityTemperatureFuture;
203 if (cavityTemperatureFuture != null) {
204 cavityTemperatureFuture.cancel(true);
210 public String toString() {
211 return "HomeConnectOvenHandler [haId: " + getThingHaId() + "]";
215 protected void resetProgramStateChannels(boolean offline) {
216 super.resetProgramStateChannels(offline);
217 getLinkedChannel(CHANNEL_REMAINING_PROGRAM_TIME_STATE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
218 getLinkedChannel(CHANNEL_PROGRAM_PROGRESS_STATE).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));
219 getLinkedChannel(CHANNEL_ELAPSED_PROGRAM_TIME).ifPresent(c -> updateState(c.getUID(), UnDefType.UNDEF));