]> git.basschouten.com Git - openhab-addons.git/blob
3cd76e9da2d168d79f069fe77b40ae81388269e1
[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.nikohomecontrol.internal.handler;
14
15 import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*;
16 import static org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.*;
17 import static org.openhab.core.types.RefreshType.REFRESH;
18
19 import java.util.HashMap;
20 import java.util.Map;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction;
25 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcActionEvent;
26 import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
27 import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType;
28 import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcAction2;
29 import org.openhab.core.library.types.IncreaseDecreaseType;
30 import org.openhab.core.library.types.OnOffType;
31 import org.openhab.core.library.types.PercentType;
32 import org.openhab.core.library.types.StopMoveType;
33 import org.openhab.core.library.types.UpDownType;
34 import org.openhab.core.thing.Bridge;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.binding.BaseThingHandler;
40 import org.openhab.core.types.Command;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * The {@link NikoHomeControlActionHandler} is responsible for handling commands, which are
46  * sent to one of the channels.
47  *
48  * @author Mark Herwege - Initial Contribution
49  */
50 @NonNullByDefault
51 public class NikoHomeControlActionHandler extends BaseThingHandler implements NhcActionEvent {
52
53     private final Logger logger = LoggerFactory.getLogger(NikoHomeControlActionHandler.class);
54
55     private volatile @Nullable NhcAction nhcAction;
56
57     private String actionId = "";
58     private int stepValue;
59     private boolean invert;
60
61     public NikoHomeControlActionHandler(Thing thing) {
62         super(thing);
63     }
64
65     @Override
66     public void handleCommand(ChannelUID channelUID, Command command) {
67         NikoHomeControlCommunication nhcComm = getCommunication();
68         if (nhcComm == null) {
69             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
70                     "Bridge communication not initialized when trying to execute action command " + actionId);
71             return;
72         }
73
74         // This can be expensive, therefore do it in a job.
75         scheduler.submit(() -> {
76             if (!nhcComm.communicationActive()) {
77                 restartCommunication(nhcComm);
78             }
79
80             if (nhcComm.communicationActive()) {
81                 handleCommandSelection(channelUID, command);
82             }
83         });
84     }
85
86     private void handleCommandSelection(ChannelUID channelUID, Command command) {
87         NhcAction nhcAction = this.nhcAction;
88         if (nhcAction == null) {
89             logger.debug("action with ID {} not initialized", actionId);
90             return;
91         }
92
93         logger.debug("handle command {} for {}", command, channelUID);
94
95         if (command == REFRESH) {
96             actionEvent(nhcAction.getState());
97             return;
98         }
99
100         switch (channelUID.getId()) {
101             case CHANNEL_BUTTON:
102             case CHANNEL_SWITCH:
103                 handleSwitchCommand(command);
104                 updateStatus(ThingStatus.ONLINE);
105                 break;
106             case CHANNEL_BRIGHTNESS:
107                 handleBrightnessCommand(command);
108                 updateStatus(ThingStatus.ONLINE);
109                 break;
110
111             case CHANNEL_ROLLERSHUTTER:
112                 handleRollershutterCommand(command);
113                 updateStatus(ThingStatus.ONLINE);
114                 break;
115
116             default:
117                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
118                         "Channel unknown " + channelUID.getId());
119         }
120     }
121
122     private void handleSwitchCommand(Command command) {
123         NhcAction nhcAction = this.nhcAction;
124         if (nhcAction == null) {
125             logger.debug("action with ID {} not initialized", actionId);
126             return;
127         }
128
129         if (command instanceof OnOffType) {
130             OnOffType s = (OnOffType) command;
131             if (OnOffType.OFF.equals(s)) {
132                 nhcAction.execute(NHCOFF);
133             } else {
134                 nhcAction.execute(NHCON);
135             }
136         }
137     }
138
139     private void handleBrightnessCommand(Command command) {
140         NhcAction nhcAction = this.nhcAction;
141         if (nhcAction == null) {
142             logger.debug("action with ID {} not initialized", actionId);
143             return;
144         }
145
146         if (command instanceof OnOffType) {
147             OnOffType s = (OnOffType) command;
148             if (OnOffType.OFF.equals(s)) {
149                 nhcAction.execute(NHCOFF);
150             } else {
151                 nhcAction.execute(NHCON);
152             }
153         } else if (command instanceof IncreaseDecreaseType) {
154             IncreaseDecreaseType s = (IncreaseDecreaseType) command;
155             int currentValue = nhcAction.getState();
156             int newValue;
157             if (IncreaseDecreaseType.INCREASE.equals(s)) {
158                 newValue = currentValue + stepValue;
159                 // round down to step multiple
160                 newValue = newValue - newValue % stepValue;
161                 nhcAction.execute(Integer.toString(newValue > 100 ? 100 : newValue));
162             } else {
163                 newValue = currentValue - stepValue;
164                 // round up to step multiple
165                 newValue = newValue + newValue % stepValue;
166                 if (newValue <= 0) {
167                     nhcAction.execute(NHCOFF);
168                 } else {
169                     nhcAction.execute(Integer.toString(newValue));
170                 }
171             }
172         } else if (command instanceof PercentType) {
173             PercentType p = (PercentType) command;
174             if (PercentType.ZERO.equals(p)) {
175                 nhcAction.execute(NHCOFF);
176             } else {
177                 nhcAction.execute(Integer.toString(p.intValue()));
178             }
179         }
180     }
181
182     private void handleRollershutterCommand(Command command) {
183         NhcAction nhcAction = this.nhcAction;
184         if (nhcAction == null) {
185             logger.debug("action with ID {} not initialized", actionId);
186             return;
187         }
188
189         if (command instanceof UpDownType) {
190             UpDownType s = (UpDownType) command;
191             if (UpDownType.UP.equals(s)) {
192                 nhcAction.execute(!invert ? NHCUP : NHCDOWN);
193             } else {
194                 nhcAction.execute(!invert ? NHCDOWN : NHCUP);
195             }
196         } else if (command instanceof StopMoveType) {
197             nhcAction.execute(NHCSTOP);
198         } else if (command instanceof PercentType) {
199             PercentType p = (PercentType) command;
200             nhcAction.execute(!invert ? Integer.toString(100 - p.intValue()) : Integer.toString(p.intValue()));
201         }
202     }
203
204     @Override
205     public void initialize() {
206         NikoHomeControlActionConfig config;
207         if (thing.getThingTypeUID().equals(THING_TYPE_DIMMABLE_LIGHT)) {
208             config = getConfig().as(NikoHomeControlActionDimmerConfig.class);
209             stepValue = ((NikoHomeControlActionDimmerConfig) config).step;
210         } else if (thing.getThingTypeUID().equals(THING_TYPE_BLIND)) {
211             config = getConfig().as(NikoHomeControlActionBlindConfig.class);
212             invert = ((NikoHomeControlActionBlindConfig) config).invert;
213         } else {
214             config = getConfig().as(NikoHomeControlActionConfig.class);
215         }
216         actionId = config.actionId;
217
218         NikoHomeControlCommunication nhcComm = getCommunication();
219         if (nhcComm == null) {
220             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
221                     "Connection with controller not started yet, could not initialize action " + actionId);
222             return;
223         }
224
225         // We need to do this in a separate thread because we may have to wait for the communication to become active
226         scheduler.submit(() -> {
227             if (!nhcComm.communicationActive()) {
228                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
229                         "No connection with controller, could not initialize action " + actionId);
230                 return;
231             }
232
233             NhcAction nhcAction = nhcComm.getActions().get(actionId);
234             if (nhcAction == null) {
235                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
236                         "Action " + actionId + " does not match an action in the controller");
237                 return;
238             }
239
240             nhcAction.setEventHandler(this);
241
242             updateProperties();
243
244             String actionLocation = nhcAction.getLocation();
245             if (thing.getLocation() == null) {
246                 thing.setLocation(actionLocation);
247             }
248
249             actionEvent(nhcAction.getState());
250
251             this.nhcAction = nhcAction;
252
253             logger.debug("action initialized {}", actionId);
254
255             Bridge bridge = getBridge();
256             if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
257                 updateStatus(ThingStatus.ONLINE);
258             } else {
259                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
260             }
261         });
262     }
263
264     private void updateProperties() {
265         NhcAction nhcAction = this.nhcAction;
266         if (nhcAction == null) {
267             logger.debug("action with ID {} not initialized", actionId);
268             return;
269         }
270
271         Map<String, String> properties = new HashMap<>();
272         properties.put("type", String.valueOf(nhcAction.getType()));
273         if (getThing().getThingTypeUID() == THING_TYPE_BLIND) {
274             properties.put("timeToOpen", String.valueOf(nhcAction.getOpenTime()));
275             properties.put("timeToClose", String.valueOf(nhcAction.getCloseTime()));
276         }
277
278         if (nhcAction instanceof NhcAction2) {
279             NhcAction2 action = (NhcAction2) nhcAction;
280             properties.put("model", action.getModel());
281             properties.put("technology", action.getTechnology());
282         }
283
284         thing.setProperties(properties);
285     }
286
287     @Override
288     public void actionEvent(int actionState) {
289         NhcAction nhcAction = this.nhcAction;
290         if (nhcAction == null) {
291             logger.debug("action with ID {} not initialized", actionId);
292             return;
293         }
294
295         ActionType actionType = nhcAction.getType();
296
297         switch (actionType) {
298             case TRIGGER:
299                 updateState(CHANNEL_BUTTON, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
300                 updateStatus(ThingStatus.ONLINE);
301             case RELAY:
302                 updateState(CHANNEL_SWITCH, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
303                 updateStatus(ThingStatus.ONLINE);
304                 break;
305             case DIMMER:
306                 updateState(CHANNEL_BRIGHTNESS, new PercentType(actionState));
307                 updateStatus(ThingStatus.ONLINE);
308                 break;
309             case ROLLERSHUTTER:
310                 updateState(CHANNEL_ROLLERSHUTTER,
311                         !invert ? new PercentType(100 - actionState) : new PercentType(actionState));
312                 updateStatus(ThingStatus.ONLINE);
313                 break;
314             default:
315                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
316                         "Unknown action type " + actionType);
317         }
318     }
319
320     @Override
321     public void actionInitialized() {
322         Bridge bridge = getBridge();
323         if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
324             updateStatus(ThingStatus.ONLINE);
325         }
326     }
327
328     @Override
329     public void actionRemoved() {
330         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
331                 "Action " + actionId + " has been removed from the controller");
332     }
333
334     private void restartCommunication(NikoHomeControlCommunication nhcComm) {
335         // We lost connection but the connection object is there, so was correctly started.
336         // Try to restart communication.
337         nhcComm.restartCommunication();
338         // If still not active, take thing offline and return.
339         if (!nhcComm.communicationActive()) {
340             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication error");
341             return;
342         }
343         // Also put the bridge back online
344         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
345         if (nhcBridgeHandler != null) {
346             nhcBridgeHandler.bridgeOnline();
347         }
348     }
349
350     private @Nullable NikoHomeControlCommunication getCommunication() {
351         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
352         if (nhcBridgeHandler == null) {
353             updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
354                     "No bridge initialized for action " + actionId);
355             return null;
356         }
357         NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
358         return nhcComm;
359     }
360
361     private @Nullable NikoHomeControlBridgeHandler getBridgeHandler() {
362         Bridge nhcBridge = getBridge();
363         if (nhcBridge == null) {
364             updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
365                     "No bridge initialized for action " + actionId);
366             return null;
367         }
368         NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();
369         return nhcBridgeHandler;
370     }
371 }