]> git.basschouten.com Git - openhab-addons.git/blob
52b274342a7ab07e8bcbe4b2f83de7d741f4ea94
[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.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 @NonNullByDefault({}) NhcAction nhcAction;
56
57     private String actionId = "";
58     private int stepValue;
59
60     public NikoHomeControlActionHandler(Thing thing) {
61         super(thing);
62     }
63
64     @Override
65     public void handleCommand(ChannelUID channelUID, Command command) {
66         NikoHomeControlCommunication nhcComm = getCommunication();
67         if (nhcComm == null) {
68             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
69                     "Niko Home Control: bridge communication not initialized when trying to execute action command "
70                             + 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         logger.debug("Niko Home Control: handle command {} for {}", command, channelUID);
88
89         if (command == REFRESH) {
90             actionEvent(nhcAction.getState());
91             return;
92         }
93
94         switch (channelUID.getId()) {
95             case CHANNEL_BUTTON:
96             case CHANNEL_SWITCH:
97                 handleSwitchCommand(command);
98                 updateStatus(ThingStatus.ONLINE);
99                 break;
100             case CHANNEL_BRIGHTNESS:
101                 handleBrightnessCommand(command);
102                 updateStatus(ThingStatus.ONLINE);
103                 break;
104
105             case CHANNEL_ROLLERSHUTTER:
106                 handleRollershutterCommand(command);
107                 updateStatus(ThingStatus.ONLINE);
108                 break;
109
110             default:
111                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
112                         "Niko Home Control: channel unknown " + channelUID.getId());
113         }
114     }
115
116     private void handleSwitchCommand(Command command) {
117         if (command instanceof OnOffType) {
118             OnOffType s = (OnOffType) command;
119             if (OnOffType.OFF.equals(s)) {
120                 nhcAction.execute(NHCOFF);
121             } else {
122                 nhcAction.execute(NHCON);
123             }
124         }
125     }
126
127     private void handleBrightnessCommand(Command command) {
128         if (command instanceof OnOffType) {
129             OnOffType s = (OnOffType) command;
130             if (OnOffType.OFF.equals(s)) {
131                 nhcAction.execute(NHCOFF);
132             } else {
133                 nhcAction.execute(NHCON);
134             }
135         } else if (command instanceof IncreaseDecreaseType) {
136             IncreaseDecreaseType s = (IncreaseDecreaseType) command;
137             int currentValue = nhcAction.getState();
138             int newValue;
139             if (IncreaseDecreaseType.INCREASE.equals(s)) {
140                 newValue = currentValue + stepValue;
141                 // round down to step multiple
142                 newValue = newValue - newValue % stepValue;
143                 nhcAction.execute(Integer.toString(newValue > 100 ? 100 : newValue));
144             } else {
145                 newValue = currentValue - stepValue;
146                 // round up to step multiple
147                 newValue = newValue + newValue % stepValue;
148                 if (newValue <= 0) {
149                     nhcAction.execute(NHCOFF);
150                 } else {
151                     nhcAction.execute(Integer.toString(newValue));
152                 }
153             }
154         } else if (command instanceof PercentType) {
155             PercentType p = (PercentType) command;
156             if (PercentType.ZERO.equals(p)) {
157                 nhcAction.execute(NHCOFF);
158             } else {
159                 nhcAction.execute(Integer.toString(p.intValue()));
160             }
161         }
162     }
163
164     private void handleRollershutterCommand(Command command) {
165         if (command instanceof UpDownType) {
166             UpDownType s = (UpDownType) command;
167             if (UpDownType.UP.equals(s)) {
168                 nhcAction.execute(NHCUP);
169             } else {
170                 nhcAction.execute(NHCDOWN);
171             }
172         } else if (command instanceof StopMoveType) {
173             nhcAction.execute(NHCSTOP);
174         } else if (command instanceof PercentType) {
175             PercentType p = (PercentType) command;
176             nhcAction.execute(Integer.toString(100 - p.intValue()));
177         }
178     }
179
180     @Override
181     public void initialize() {
182         NikoHomeControlActionConfig config;
183         if (thing.getThingTypeUID().equals(THING_TYPE_DIMMABLE_LIGHT)) {
184             config = getConfig().as(NikoHomeControlActionDimmerConfig.class);
185             stepValue = ((NikoHomeControlActionDimmerConfig) config).step;
186         } else {
187             config = getConfig().as(NikoHomeControlActionConfig.class);
188         }
189         actionId = config.actionId;
190
191         NikoHomeControlCommunication nhcComm = getCommunication();
192         if (nhcComm == null) {
193             return;
194         }
195
196         // We need to do this in a separate thread because we may have to wait for the communication to become active
197         scheduler.submit(() -> {
198             if (!nhcComm.communicationActive()) {
199                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
200                         "Niko Home Control: no connection with Niko Home Control, could not initialize action "
201                                 + actionId);
202                 return;
203             }
204
205             nhcAction = nhcComm.getActions().get(actionId);
206             if (nhcAction == null) {
207                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
208                         "Niko Home Control: actionId does not match an action in the controller " + actionId);
209                 return;
210             }
211
212             nhcAction.setEventHandler(this);
213
214             updateProperties();
215
216             String actionLocation = nhcAction.getLocation();
217             if (thing.getLocation() == null) {
218                 thing.setLocation(actionLocation);
219             }
220
221             actionEvent(nhcAction.getState());
222
223             logger.debug("Niko Home Control: action initialized {}", actionId);
224
225             Bridge bridge = getBridge();
226             if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
227                 updateStatus(ThingStatus.ONLINE);
228             } else {
229                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
230             }
231         });
232     }
233
234     private void updateProperties() {
235         Map<String, String> properties = new HashMap<>();
236         properties.put("type", String.valueOf(nhcAction.getType()));
237         if (getThing().getThingTypeUID() == THING_TYPE_BLIND) {
238             properties.put("timeToOpen", String.valueOf(nhcAction.getOpenTime()));
239             properties.put("timeToClose", String.valueOf(nhcAction.getCloseTime()));
240         }
241
242         if (nhcAction instanceof NhcAction2) {
243             NhcAction2 action = (NhcAction2) nhcAction;
244             properties.put("model", action.getModel());
245             properties.put("technology", action.getTechnology());
246         }
247
248         thing.setProperties(properties);
249     }
250
251     @Override
252     public void actionEvent(int actionState) {
253         ActionType actionType = nhcAction.getType();
254
255         switch (actionType) {
256             case TRIGGER:
257                 updateState(CHANNEL_BUTTON, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
258                 updateStatus(ThingStatus.ONLINE);
259             case RELAY:
260                 updateState(CHANNEL_SWITCH, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
261                 updateStatus(ThingStatus.ONLINE);
262                 break;
263             case DIMMER:
264                 updateState(CHANNEL_BRIGHTNESS, new PercentType(actionState));
265                 updateStatus(ThingStatus.ONLINE);
266                 break;
267             case ROLLERSHUTTER:
268                 updateState(CHANNEL_ROLLERSHUTTER, new PercentType(actionState));
269                 updateStatus(ThingStatus.ONLINE);
270                 break;
271             default:
272                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
273                         "Niko Home Control: unknown action type " + actionType);
274         }
275     }
276
277     @Override
278     public void actionRemoved() {
279         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
280                 "Niko Home Control: action has been removed from the controller " + actionId);
281     }
282
283     private void restartCommunication(NikoHomeControlCommunication nhcComm) {
284         // We lost connection but the connection object is there, so was correctly started.
285         // Try to restart communication.
286         nhcComm.restartCommunication();
287         // If still not active, take thing offline and return.
288         if (!nhcComm.communicationActive()) {
289             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
290                     "Niko Home Control: communication socket error");
291             return;
292         }
293         // Also put the bridge back online
294         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
295         if (nhcBridgeHandler != null) {
296             nhcBridgeHandler.bridgeOnline();
297         }
298     }
299
300     private @Nullable NikoHomeControlCommunication getCommunication() {
301         NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
302         if (nhcBridgeHandler == null) {
303             updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
304                     "Niko Home Control: no bridge initialized for action " + actionId);
305             return null;
306         }
307         NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
308         return nhcComm;
309     }
310
311     private @Nullable NikoHomeControlBridgeHandler getBridgeHandler() {
312         Bridge nhcBridge = getBridge();
313         if (nhcBridge == null) {
314             updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED,
315                     "Niko Home Control: no bridge initialized for action " + actionId);
316             return null;
317         }
318         NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();
319         return nhcBridgeHandler;
320     }
321 }