]> git.basschouten.com Git - openhab-addons.git/blob
f5e5bc06a61969bac85a93d89f1e4fcca89ee0a6
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.nuki.internal.handler;
14
15 import java.util.concurrent.ScheduledFuture;
16 import java.util.concurrent.TimeUnit;
17
18 import org.openhab.binding.nuki.internal.NukiBindingConstants;
19 import org.openhab.binding.nuki.internal.converter.LockActionConverter;
20 import org.openhab.binding.nuki.internal.dataexchange.BridgeLockActionResponse;
21 import org.openhab.binding.nuki.internal.dataexchange.BridgeLockStateResponse;
22 import org.openhab.binding.nuki.internal.dataexchange.NukiBaseResponse;
23 import org.openhab.binding.nuki.internal.dataexchange.NukiHttpClient;
24 import org.openhab.core.config.core.Configuration;
25 import org.openhab.core.library.types.DecimalType;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.thing.Bridge;
28 import org.openhab.core.thing.Channel;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.ThingStatusInfo;
34 import org.openhab.core.thing.binding.BaseThingHandler;
35 import org.openhab.core.thing.binding.ThingHandler;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.RefreshType;
38 import org.openhab.core.types.State;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * The {@link NukiSmartLockHandler} is responsible for handling commands, which are
44  * sent to one of the channels.
45  *
46  * @author Markus Katter - Initial contribution
47  * @contributer Christian Hoefler - Door sensor integration
48  */
49 public class NukiSmartLockHandler extends BaseThingHandler {
50
51     private final Logger logger = LoggerFactory.getLogger(NukiSmartLockHandler.class);
52     private static final int JOB_INTERVAL = 60;
53
54     private NukiHttpClient nukiHttpClient;
55     private ScheduledFuture<?> reInitJob;
56     private String nukiId;
57     private boolean unlatch;
58
59     public NukiSmartLockHandler(Thing thing) {
60         super(thing);
61         logger.debug("Instantiating NukiSmartLockHandler({})", thing);
62     }
63
64     @Override
65     public void initialize() {
66         logger.debug("initialize() for Smart Lock[{}].", getThing().getUID());
67         Configuration config = getConfig();
68         nukiId = (String) config.get(NukiBindingConstants.CONFIG_NUKI_ID);
69         unlatch = (Boolean) config.get(NukiBindingConstants.CONFIG_UNLATCH);
70         if (nukiId == null) {
71             logger.debug("NukiSmartLockHandler[{}] is not initializable, nukiId setting is unset in the configuration!",
72                     getThing().getUID());
73             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "nukiId setting is unset");
74         } else {
75             scheduler.execute(this::initializeHandler);
76         }
77     }
78
79     @Override
80     public void dispose() {
81         logger.debug("dispose() for Smart Lock[{}].", getThing().getUID());
82         stopReInitJob();
83     }
84
85     private void initializeHandler() {
86         logger.debug("initializeHandler() for Smart Lock[{}]", nukiId);
87         Bridge bridge = getBridge();
88         if (bridge == null) {
89             initializeHandler(null, null);
90         } else {
91             initializeHandler(bridge.getHandler(), bridge.getStatus());
92         }
93     }
94
95     private void initializeHandler(ThingHandler bridgeHandler, ThingStatus bridgeStatus) {
96         if (bridgeHandler != null && bridgeStatus != null) {
97             if (bridgeStatus == ThingStatus.ONLINE) {
98                 nukiHttpClient = ((NukiBridgeHandler) bridgeHandler).getNukiHttpClient();
99                 BridgeLockStateResponse bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
100                 if (handleResponse(bridgeLockStateResponse, null, null)) {
101                     updateStatus(ThingStatus.ONLINE);
102                     for (Channel channel : thing.getChannels()) {
103                         handleCommand(channel.getUID(), RefreshType.REFRESH);
104                     }
105                     stopReInitJob();
106                 }
107             } else {
108                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
109                 stopReInitJob();
110             }
111         } else {
112             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
113             stopReInitJob();
114         }
115     }
116
117     @Override
118     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
119         logger.debug("bridgeStatusChanged({}) for Smart Lock[{}].", bridgeStatusInfo, nukiId);
120         scheduler.execute(() -> {
121             Bridge bridge = getBridge();
122             if (bridge == null) {
123                 initializeHandler(null, bridgeStatusInfo.getStatus());
124             } else {
125                 initializeHandler(bridge.getHandler(), bridgeStatusInfo.getStatus());
126             }
127         });
128     }
129
130     @Override
131     public void handleCommand(ChannelUID channelUID, Command command) {
132         logger.debug("handleCommand({}, {})", channelUID, command);
133
134         if (getThing().getStatus() != ThingStatus.ONLINE) {
135             logger.debug("Thing is not ONLINE; command[{}] for channelUID[{}] is ignored", command, channelUID);
136             return;
137         }
138
139         if (command instanceof RefreshType) {
140             handleCommandRefreshType(channelUID, command);
141             return;
142         }
143
144         boolean validCmd = true;
145         switch (channelUID.getId()) {
146             case NukiBindingConstants.CHANNEL_SMARTLOCK_LOCK:
147                 if (command instanceof OnOffType) {
148                     int lockAction;
149                     if (unlatch) {
150                         lockAction = (command == OnOffType.OFF ? NukiBindingConstants.LOCK_ACTIONS_UNLATCH
151                                 : NukiBindingConstants.LOCK_ACTIONS_LOCK);
152                     } else {
153                         lockAction = (command == OnOffType.OFF ? NukiBindingConstants.LOCK_ACTIONS_UNLOCK
154                                 : NukiBindingConstants.LOCK_ACTIONS_LOCK);
155                     }
156                     Channel channelLockState = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_STATE);
157                     if (channelLockState != null) {
158                         updateState(channelLockState.getUID(),
159                                 new DecimalType(LockActionConverter.getLockStateFor(lockAction)));
160                     }
161                     BridgeLockActionResponse bridgeLockActionResponse = nukiHttpClient.getBridgeLockAction(nukiId,
162                             lockAction);
163                     handleResponse(bridgeLockActionResponse, channelUID.getAsString(), command.toString());
164                 } else {
165                     validCmd = false;
166                 }
167                 break;
168             case NukiBindingConstants.CHANNEL_SMARTLOCK_STATE:
169                 if (command instanceof DecimalType) {
170                     int lockAction;
171                     lockAction = ((DecimalType) command).intValue();
172                     lockAction = LockActionConverter.getLockActionFor(lockAction);
173                     updateState(channelUID, new DecimalType(LockActionConverter.getLockStateFor(lockAction)));
174                     BridgeLockActionResponse bridgeLockActionResponse = nukiHttpClient.getBridgeLockAction(nukiId,
175                             lockAction);
176                     handleResponse(bridgeLockActionResponse, channelUID.getAsString(), command.toString());
177                 } else {
178                     validCmd = false;
179                 }
180                 break;
181             default:
182                 validCmd = false;
183                 break;
184         }
185         if (!validCmd) {
186             logger.debug("Unexpected command[{}] for channelUID[{}]!", command, channelUID);
187         }
188     }
189
190     private void handleCommandRefreshType(ChannelUID channelUID, Command command) {
191         logger.debug("handleCommandRefreshType({}, {})", channelUID, command);
192         BridgeLockStateResponse bridgeLockStateResponse;
193         switch (channelUID.getId()) {
194             case NukiBindingConstants.CHANNEL_SMARTLOCK_LOCK:
195                 bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
196                 if (handleResponse(bridgeLockStateResponse, channelUID.getAsString(), command.toString())) {
197                     int lockState = bridgeLockStateResponse.getState();
198                     State state;
199                     if (lockState == NukiBindingConstants.LOCK_STATES_LOCKED) {
200                         state = OnOffType.ON;
201                     } else if (lockState == NukiBindingConstants.LOCK_STATES_UNLOCKED) {
202                         state = OnOffType.OFF;
203                     } else {
204                         logger.warn(
205                                 "Smart Lock returned lockState[{}]. Intentionally setting possibly wrong value 'OFF' for channel 'smartlockLock'!",
206                                 lockState);
207                         state = OnOffType.OFF;
208                     }
209                     updateState(channelUID, state);
210                 }
211                 break;
212             case NukiBindingConstants.CHANNEL_SMARTLOCK_STATE:
213                 bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
214                 if (handleResponse(bridgeLockStateResponse, channelUID.getAsString(), command.toString())) {
215                     updateState(channelUID, new DecimalType(bridgeLockStateResponse.getState()));
216                 }
217                 break;
218             case NukiBindingConstants.CHANNEL_SMARTLOCK_LOW_BATTERY:
219                 bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
220                 if (handleResponse(bridgeLockStateResponse, channelUID.getAsString(), command.toString())) {
221                     updateState(channelUID, bridgeLockStateResponse.isBatteryCritical() ? OnOffType.ON : OnOffType.OFF);
222                 }
223                 break;
224             case NukiBindingConstants.CHANNEL_SMARTLOCK_DOOR_STATE:
225                 bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
226                 if (handleResponse(bridgeLockStateResponse, channelUID.getAsString(), command.toString())) {
227                     updateState(channelUID, new DecimalType(bridgeLockStateResponse.getDoorsensorState()));
228                 }
229                 break;
230             default:
231                 logger.debug("Command[{}] for channelUID[{}] not implemented!", command, channelUID);
232                 return;
233         }
234     }
235
236     private boolean handleResponse(NukiBaseResponse nukiBaseResponse, String channelUID, String command) {
237         if (nukiBaseResponse.getStatus() == 200 && nukiBaseResponse.isSuccess()) {
238             logger.debug("Command[{}] succeeded for channelUID[{}] on nukiId[{}]!", command, channelUID, nukiId);
239             return true;
240         } else if (nukiBaseResponse.getStatus() != 200) {
241             logger.debug("Request to Bridge failed! status[{}] - message[{}]", nukiBaseResponse.getStatus(),
242                     nukiBaseResponse.getMessage());
243         } else if (!nukiBaseResponse.isSuccess()) {
244             logger.debug(
245                     "Request from Bridge to Smart Lock failed! status[{}] - message[{}] - isSuccess[{}]. Check if Nuki Smart Lock is powered on!",
246                     nukiBaseResponse.getStatus(), nukiBaseResponse.getMessage(), nukiBaseResponse.isSuccess());
247         }
248         logger.debug("Could not handle command[{}] for channelUID[{}] on nukiId[{}]!", command, channelUID, nukiId);
249         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, nukiBaseResponse.getMessage());
250         Channel channelLock = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_LOCK);
251         if (channelLock != null) {
252             updateState(channelLock.getUID(), OnOffType.OFF);
253         }
254         Channel channelLockState = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_STATE);
255         if (channelLockState != null) {
256             updateState(channelLockState.getUID(), new DecimalType(NukiBindingConstants.LOCK_STATES_UNDEFINED));
257         }
258         Channel channelDoorState = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_DOOR_STATE);
259         if (channelDoorState != null) {
260             updateState(channelDoorState.getUID(), new DecimalType(NukiBindingConstants.DOORSENSOR_STATES_UNKNOWN));
261         }
262         startReInitJob();
263         return false;
264     }
265
266     private void startReInitJob() {
267         logger.trace("Starting reInitJob with interval of  {}secs for Smart Lock[{}].", JOB_INTERVAL, nukiId);
268         if (reInitJob != null) {
269             logger.trace("Already started reInitJob for Smart Lock[{}].", nukiId);
270             return;
271         }
272         reInitJob = scheduler.scheduleWithFixedDelay(this::initializeHandler, JOB_INTERVAL, JOB_INTERVAL,
273                 TimeUnit.SECONDS);
274     }
275
276     private void stopReInitJob() {
277         logger.trace("Stopping reInitJob for Smart Lock[{}].", nukiId);
278         if (reInitJob != null && !reInitJob.isCancelled()) {
279             logger.trace("Stopped reInitJob for Smart Lock[{}].", nukiId);
280             reInitJob.cancel(true);
281         }
282         reInitJob = null;
283     }
284
285     public void handleApiServletUpdate(ChannelUID channelUID, State newState) {
286         logger.trace("handleApiServletUpdate({}, {})", channelUID, newState);
287         updateState(channelUID, newState);
288     }
289 }