]> git.basschouten.com Git - openhab-addons.git/blob
7fd763756a683c19b5600f05d50301ac84a2c962
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.homeconnect.internal.handler;
14
15 import static org.openhab.binding.homeconnect.internal.HomeConnectBindingConstants.*;
16 import static org.openhab.core.library.unit.Units.SECOND;
17
18 import java.util.Arrays;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Optional;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
24
25 import javax.measure.IncommensurableException;
26 import javax.measure.UnconvertibleException;
27 import javax.measure.quantity.Time;
28
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;
45
46 /**
47  * The {@link HomeConnectOvenHandler} is responsible for handling commands, which are
48  * sent to one of the channels of an oven.
49  *
50  * @author Jonas BrĂ¼stel - Initial contribution
51  */
52 @NonNullByDefault
53 public class HomeConnectOvenHandler extends AbstractHomeConnectThingHandler {
54
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;
58
59     private final Logger logger = LoggerFactory.getLogger(HomeConnectOvenHandler.class);
60
61     private @Nullable ScheduledFuture<?> cavityTemperatureFuture;
62     private boolean manuallyUpdateCavityTemperature;
63
64     public HomeConnectOvenHandler(Thing thing,
65             HomeConnectDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
66         super(thing, dynamicStateDescriptionProvider);
67         manuallyUpdateCavityTemperature = true;
68     }
69
70     @Override
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());
80
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()));
88                     }
89                     return UnDefType.UNDEF;
90                 })));
91         handlers.put(CHANNEL_SETPOINT_TEMPERATURE, getAndUpdateSelectedProgramStateUpdateHandler());
92         handlers.put(CHANNEL_DURATION, getAndUpdateSelectedProgramStateUpdateHandler());
93     }
94
95     @Override
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());
108
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;
114             }
115         });
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()))));
119
120             if (STATE_POWER_ON.equals(event.getValue())) {
121                 updateChannels();
122             } else {
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));
129             }
130         });
131
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()))));
136         });
137
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))));
144     }
145
146     @Override
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);
151
152         handlePowerCommand(channelUID, command, apiClient, STATE_POWER_STANDBY);
153
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);
162
163                 try {
164                     String value = String
165                             .valueOf(quantity.getUnit().getConverterToAny(SECOND).convert(quantity).intValue());
166                     logger.debug("Set duration to {} seconds. haId={}", value, getThingHaId());
167
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());
171                 }
172             }
173         } else {
174             logger.debug("Device can not handle command {} in current operation state ({}). haId={}", command,
175                     operationState, getThingHaId());
176         }
177     }
178
179     @Override
180     public void initialize() {
181         super.initialize();
182         cavityTemperatureFuture = scheduler.scheduleWithFixedDelay(() -> {
183             String operationState = getOperationState();
184             boolean manuallyUpdateCavityTemperature = this.manuallyUpdateCavityTemperature;
185
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());
191                     } else {
192                         logger.debug("Update cavity temperature via SSE, don't need to fetch manually. haId={}",
193                                 getThingHaId());
194                     }
195                 });
196             }
197         }, CAVITY_TEMPERATURE_SCHEDULER_INITIAL_DELAY, CAVITY_TEMPERATURE_SCHEDULER_PERIOD, TimeUnit.SECONDS);
198     }
199
200     @Override
201     public void dispose() {
202         ScheduledFuture<?> cavityTemperatureFuture = this.cavityTemperatureFuture;
203         if (cavityTemperatureFuture != null) {
204             cavityTemperatureFuture.cancel(true);
205         }
206         super.dispose();
207     }
208
209     @Override
210     public String toString() {
211         return "HomeConnectOvenHandler [haId: " + getThingHaId() + "]";
212     }
213
214     @Override
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));
220     }
221 }