]> git.basschouten.com Git - openhab-addons.git/blob
a2236427b8b29ee23b3c3e77a056611421181e64
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.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;
46
47 /**
48  * The {@link HomeConnectOvenHandler} is responsible for handling commands, which are
49  * sent to one of the channels of a oven.
50  *
51  * @author Jonas BrĂ¼stel - Initial contribution
52  */
53 @NonNullByDefault
54 public class HomeConnectOvenHandler extends AbstractHomeConnectThingHandler {
55
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;
59
60     private final Logger logger = LoggerFactory.getLogger(HomeConnectOvenHandler.class);
61
62     private @Nullable ScheduledFuture<?> cavityTemperatureFuture;
63     private boolean manuallyUpdateCavityTemperature;
64
65     public HomeConnectOvenHandler(Thing thing,
66             HomeConnectDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
67         super(thing, dynamicStateDescriptionProvider);
68         manuallyUpdateCavityTemperature = true;
69     }
70
71     @Override
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());
81
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()));
89                     }
90                     return UnDefType.UNDEF;
91                 })));
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);
96             }
97         });
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);
102             }
103         });
104     }
105
106     @Override
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());
118
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;
124             }
125         });
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()))));
129
130             if (STATE_POWER_ON.equals(event.getValue())) {
131                 updateChannels();
132             } else {
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));
139             }
140         });
141
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()))));
146         });
147
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))));
154     }
155
156     @Override
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);
161
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);
166         }
167
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);
176
177                 try {
178                     String value = String
179                             .valueOf(quantity.getUnit().getConverterToAny(SECOND).convert(quantity).intValue());
180                     logger.debug("Set duration to {} seconds. haId={}", value, getThingHaId());
181
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());
185                 }
186             }
187         } else {
188             logger.debug("Device can not handle command {} in current operation state ({}). haId={}", command,
189                     operationState, getThingHaId());
190         }
191     }
192
193     @Override
194     public void initialize() {
195         super.initialize();
196         cavityTemperatureFuture = scheduler.scheduleWithFixedDelay(() -> {
197             String operationState = getOperationState();
198             boolean manuallyUpdateCavityTemperature = this.manuallyUpdateCavityTemperature;
199
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());
205                     } else {
206                         logger.debug("Update cavity temperature via SSE, don't need to fetch manually. haId={}",
207                                 getThingHaId());
208                     }
209                 });
210             }
211         }, CAVITY_TEMPERATURE_SCHEDULER_INITIAL_DELAY, CAVITY_TEMPERATURE_SCHEDULER_PERIOD, TimeUnit.SECONDS);
212     }
213
214     @Override
215     public void dispose() {
216         ScheduledFuture<?> cavityTemperatureFuture = this.cavityTemperatureFuture;
217         if (cavityTemperatureFuture != null) {
218             cavityTemperatureFuture.cancel(true);
219         }
220         super.dispose();
221     }
222
223     @Override
224     public String toString() {
225         return "HomeConnectOvenHandler [haId: " + getThingHaId() + "]";
226     }
227
228     @Override
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));
234     }
235 }