]> git.basschouten.com Git - openhab-addons.git/blob
df5bb9544e2e83b1748fd04333ab001ece040b52
[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(nhcAction);
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(NhcAction nhcAction) {
264         Map<String, String> properties = new HashMap<>();
265
266         properties.put("type", String.valueOf(nhcAction.getType()));
267         if (getThing().getThingTypeUID() == THING_TYPE_BLIND) {
268             properties.put("timeToOpen", String.valueOf(nhcAction.getOpenTime()));
269             properties.put("timeToClose", String.valueOf(nhcAction.getCloseTime()));
270         }
271
272         if (nhcAction instanceof NhcAction2) {
273             NhcAction2 action = (NhcAction2) nhcAction;
274             properties.put(PROPERTY_DEVICE_TYPE, action.getDeviceType());
275             properties.put(PROPERTY_DEVICE_TECHNOLOGY, action.getDeviceTechnology());
276             properties.put(PROPERTY_DEVICE_MODEL, action.getDeviceModel());
277         }
278
279         thing.setProperties(properties);
280     }
281
282     @Override
283     public void actionEvent(int actionState) {
284         NhcAction nhcAction = this.nhcAction;
285         if (nhcAction == null) {
286             logger.debug("action with ID {} not initialized", actionId);
287             return;
288         }
289
290         ActionType actionType = nhcAction.getType();
291
292         switch (actionType) {
293             case TRIGGER:
294                 updateState(CHANNEL_BUTTON, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
295                 updateStatus(ThingStatus.ONLINE);
296             case RELAY:
297                 updateState(CHANNEL_SWITCH, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
298                 updateStatus(ThingStatus.ONLINE);
299                 break;
300             case DIMMER:
301                 updateState(CHANNEL_BRIGHTNESS, new PercentType(actionState));
302                 updateStatus(ThingStatus.ONLINE);
303                 break;
304             case ROLLERSHUTTER:
305                 updateState(CHANNEL_ROLLERSHUTTER,
306                         !invert ? new PercentType(100 - actionState) : new PercentType(actionState));
307                 updateStatus(ThingStatus.ONLINE);
308                 break;
309             default:
310                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
311                         "@text/offline.configuration-error.actionType");
312         }
313     }
314
315     @Override
316     public void actionInitialized() {
317         Bridge bridge = getBridge();
318         if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
319             updateStatus(ThingStatus.ONLINE);
320         }
321     }
322
323     @Override
324     public void actionRemoved() {
325         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
326                 "@text/offline.configuration-error.actionRemoved");
327     }
328
329     private void restartCommunication(NikoHomeControlCommunication nhcComm) {
330         // We lost connection but the connection object is there, so was correctly started.
331         // Try to restart communication.
332         nhcComm.restartCommunication();
333         // If still not active, take thing offline and return.
334         if (!nhcComm.communicationActive()) {
335             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
336                     "@text/offline.communication-error");
337             return;
338         }
339         // Also put the bridge back online
340         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
341         if (nhcBridgeHandler != null) {
342             nhcBridgeHandler.bridgeOnline();
343         } else {
344             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
345                     "@text/offline.bridge-unitialized");
346         }
347     }
348
349     private @Nullable NikoHomeControlCommunication getCommunication() {
350         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
351         return nhcBridgeHandler != null ? nhcBridgeHandler.getCommunication() : null;
352     }
353
354     private @Nullable NikoHomeControlBridgeHandler getBridgeHandler() {
355         Bridge nhcBridge = getBridge();
356         return nhcBridge != null ? (NikoHomeControlBridgeHandler) nhcBridge.getHandler() : null;
357     }
358 }