]> git.basschouten.com Git - openhab-addons.git/blob
d96c172fa5ffcacfd2260743fdae8448f91c5899
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.BRIDGE_UNINITIALIZED,
70                     "@text/offline.bridge-unitialized");
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 (REFRESH.equals(command)) {
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             case CHANNEL_ROLLERSHUTTER:
111                 handleRollershutterCommand(command);
112                 updateStatus(ThingStatus.ONLINE);
113                 break;
114             default:
115                 logger.debug("unexpected command for channel {}", channelUID.getId());
116         }
117     }
118
119     private void handleSwitchCommand(Command command) {
120         NhcAction nhcAction = this.nhcAction;
121         if (nhcAction == null) {
122             logger.debug("action with ID {} not initialized", actionId);
123             return;
124         }
125
126         if (command instanceof OnOffType) {
127             OnOffType s = (OnOffType) command;
128             if (OnOffType.OFF.equals(s)) {
129                 nhcAction.execute(NHCOFF);
130             } else {
131                 nhcAction.execute(NHCON);
132             }
133         }
134     }
135
136     private void handleBrightnessCommand(Command command) {
137         NhcAction nhcAction = this.nhcAction;
138         if (nhcAction == null) {
139             logger.debug("action with ID {} not initialized", actionId);
140             return;
141         }
142
143         if (command instanceof OnOffType) {
144             OnOffType s = (OnOffType) command;
145             if (OnOffType.OFF.equals(s)) {
146                 nhcAction.execute(NHCOFF);
147             } else {
148                 nhcAction.execute(NHCON);
149             }
150         } else if (command instanceof IncreaseDecreaseType) {
151             IncreaseDecreaseType s = (IncreaseDecreaseType) command;
152             int currentValue = nhcAction.getState();
153             int newValue;
154             if (IncreaseDecreaseType.INCREASE.equals(s)) {
155                 newValue = currentValue + stepValue;
156                 // round down to step multiple
157                 newValue = newValue - newValue % stepValue;
158                 nhcAction.execute(Integer.toString(newValue > 100 ? 100 : newValue));
159             } else {
160                 newValue = currentValue - stepValue;
161                 // round up to step multiple
162                 newValue = newValue + newValue % stepValue;
163                 if (newValue <= 0) {
164                     nhcAction.execute(NHCOFF);
165                 } else {
166                     nhcAction.execute(Integer.toString(newValue));
167                 }
168             }
169         } else if (command instanceof PercentType) {
170             PercentType p = (PercentType) command;
171             if (PercentType.ZERO.equals(p)) {
172                 nhcAction.execute(NHCOFF);
173             } else {
174                 nhcAction.execute(Integer.toString(p.intValue()));
175             }
176         }
177     }
178
179     private void handleRollershutterCommand(Command command) {
180         NhcAction nhcAction = this.nhcAction;
181         if (nhcAction == null) {
182             logger.debug("action with ID {} not initialized", actionId);
183             return;
184         }
185
186         if (command instanceof UpDownType) {
187             UpDownType s = (UpDownType) command;
188             if (UpDownType.UP.equals(s)) {
189                 nhcAction.execute(!invert ? NHCUP : NHCDOWN);
190             } else {
191                 nhcAction.execute(!invert ? NHCDOWN : NHCUP);
192             }
193         } else if (command instanceof StopMoveType) {
194             nhcAction.execute(NHCSTOP);
195         } else if (command instanceof PercentType) {
196             PercentType p = (PercentType) command;
197             nhcAction.execute(!invert ? Integer.toString(100 - p.intValue()) : Integer.toString(p.intValue()));
198         }
199     }
200
201     @Override
202     public void initialize() {
203         NikoHomeControlActionConfig config;
204         if (thing.getThingTypeUID().equals(THING_TYPE_DIMMABLE_LIGHT)) {
205             config = getConfig().as(NikoHomeControlActionDimmerConfig.class);
206             stepValue = ((NikoHomeControlActionDimmerConfig) config).step;
207         } else if (thing.getThingTypeUID().equals(THING_TYPE_BLIND)) {
208             config = getConfig().as(NikoHomeControlActionBlindConfig.class);
209             invert = ((NikoHomeControlActionBlindConfig) config).invert;
210         } else {
211             config = getConfig().as(NikoHomeControlActionConfig.class);
212         }
213         actionId = config.actionId;
214
215         NikoHomeControlCommunication nhcComm = getCommunication();
216         if (nhcComm == null) {
217             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
218                     "@text/offline.bridge-unitialized");
219             return;
220         } else {
221             updateStatus(ThingStatus.UNKNOWN);
222         }
223
224         // We need to do this in a separate thread because we may have to wait for the communication to become active
225         scheduler.submit(() -> {
226             if (!nhcComm.communicationActive()) {
227                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
228                         "@text/offline.communication-error");
229                 return;
230             }
231
232             NhcAction nhcAction = nhcComm.getActions().get(actionId);
233             if (nhcAction == null) {
234                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
235                         "@text/offline.configuration-error.actionId");
236                 return;
237             }
238
239             nhcAction.setEventHandler(this);
240
241             updateProperties();
242
243             String actionLocation = nhcAction.getLocation();
244             if (thing.getLocation() == null) {
245                 thing.setLocation(actionLocation);
246             }
247
248             actionEvent(nhcAction.getState());
249
250             this.nhcAction = nhcAction;
251
252             logger.debug("action initialized {}", actionId);
253
254             Bridge bridge = getBridge();
255             if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
256                 updateStatus(ThingStatus.ONLINE);
257             } else {
258                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
259             }
260         });
261     }
262
263     private void updateProperties() {
264         NhcAction nhcAction = this.nhcAction;
265         if (nhcAction == null) {
266             logger.debug("action with ID {} not initialized", actionId);
267             return;
268         }
269
270         Map<String, String> properties = new HashMap<>();
271         properties.put("type", String.valueOf(nhcAction.getType()));
272         if (getThing().getThingTypeUID() == THING_TYPE_BLIND) {
273             properties.put("timeToOpen", String.valueOf(nhcAction.getOpenTime()));
274             properties.put("timeToClose", String.valueOf(nhcAction.getCloseTime()));
275         }
276
277         if (nhcAction instanceof NhcAction2) {
278             NhcAction2 action = (NhcAction2) nhcAction;
279             properties.put("model", action.getModel());
280             properties.put("technology", action.getTechnology());
281         }
282
283         thing.setProperties(properties);
284     }
285
286     @Override
287     public void actionEvent(int actionState) {
288         NhcAction nhcAction = this.nhcAction;
289         if (nhcAction == null) {
290             logger.debug("action with ID {} not initialized", actionId);
291             return;
292         }
293
294         ActionType actionType = nhcAction.getType();
295
296         switch (actionType) {
297             case TRIGGER:
298                 updateState(CHANNEL_BUTTON, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
299                 updateStatus(ThingStatus.ONLINE);
300             case RELAY:
301                 updateState(CHANNEL_SWITCH, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
302                 updateStatus(ThingStatus.ONLINE);
303                 break;
304             case DIMMER:
305                 updateState(CHANNEL_BRIGHTNESS, new PercentType(actionState));
306                 updateStatus(ThingStatus.ONLINE);
307                 break;
308             case ROLLERSHUTTER:
309                 updateState(CHANNEL_ROLLERSHUTTER,
310                         !invert ? new PercentType(100 - actionState) : new PercentType(actionState));
311                 updateStatus(ThingStatus.ONLINE);
312                 break;
313             default:
314                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
315                         "@text/offline.configuration-error.actionType");
316         }
317     }
318
319     @Override
320     public void actionInitialized() {
321         Bridge bridge = getBridge();
322         if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
323             updateStatus(ThingStatus.ONLINE);
324         }
325     }
326
327     @Override
328     public void actionRemoved() {
329         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
330                 "@text/offline.configuration-error.actionRemoved");
331     }
332
333     private void restartCommunication(NikoHomeControlCommunication nhcComm) {
334         // We lost connection but the connection object is there, so was correctly started.
335         // Try to restart communication.
336         nhcComm.restartCommunication();
337         // If still not active, take thing offline and return.
338         if (!nhcComm.communicationActive()) {
339             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
340                     "@text/offline.communication-error");
341             return;
342         }
343         // Also put the bridge back online
344         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
345         if (nhcBridgeHandler != null) {
346             nhcBridgeHandler.bridgeOnline();
347         } else {
348             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
349                     "@text/offline.bridge-unitialized");
350         }
351     }
352
353     private @Nullable NikoHomeControlCommunication getCommunication() {
354         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
355         return nhcBridgeHandler != null ? nhcBridgeHandler.getCommunication() : null;
356     }
357
358     private @Nullable NikoHomeControlBridgeHandler getBridgeHandler() {
359         Bridge nhcBridge = getBridge();
360         return nhcBridge != null ? (NikoHomeControlBridgeHandler) nhcBridge.getHandler() : null;
361     }
362 }