2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.nikohomecontrol.internal.handler;
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;
19 import java.util.HashMap;
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;
45 * The {@link NikoHomeControlActionHandler} is responsible for handling commands, which are
46 * sent to one of the channels.
48 * @author Mark Herwege - Initial Contribution
51 public class NikoHomeControlActionHandler extends BaseThingHandler implements NhcActionEvent {
53 private final Logger logger = LoggerFactory.getLogger(NikoHomeControlActionHandler.class);
55 private volatile @NonNullByDefault({}) NhcAction nhcAction;
57 private String actionId = "";
58 private int stepValue;
60 public NikoHomeControlActionHandler(Thing thing) {
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 "
74 // This can be expensive, therefore do it in a job.
75 scheduler.submit(() -> {
76 if (!nhcComm.communicationActive()) {
77 restartCommunication(nhcComm);
80 if (nhcComm.communicationActive()) {
81 handleCommandSelection(channelUID, command);
86 private void handleCommandSelection(ChannelUID channelUID, Command command) {
87 logger.debug("Niko Home Control: handle command {} for {}", command, channelUID);
89 if (command == REFRESH) {
90 actionEvent(nhcAction.getState());
94 switch (channelUID.getId()) {
97 handleSwitchCommand(command);
98 updateStatus(ThingStatus.ONLINE);
100 case CHANNEL_BRIGHTNESS:
101 handleBrightnessCommand(command);
102 updateStatus(ThingStatus.ONLINE);
105 case CHANNEL_ROLLERSHUTTER:
106 handleRollershutterCommand(command);
107 updateStatus(ThingStatus.ONLINE);
111 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
112 "Niko Home Control: channel unknown " + channelUID.getId());
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);
122 nhcAction.execute(NHCON);
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);
133 nhcAction.execute(NHCON);
135 } else if (command instanceof IncreaseDecreaseType) {
136 IncreaseDecreaseType s = (IncreaseDecreaseType) command;
137 int currentValue = nhcAction.getState();
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));
145 newValue = currentValue - stepValue;
146 // round up to step multiple
147 newValue = newValue + newValue % stepValue;
149 nhcAction.execute(NHCOFF);
151 nhcAction.execute(Integer.toString(newValue));
154 } else if (command instanceof PercentType) {
155 PercentType p = (PercentType) command;
156 if (PercentType.ZERO.equals(p)) {
157 nhcAction.execute(NHCOFF);
159 nhcAction.execute(Integer.toString(p.intValue()));
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);
170 nhcAction.execute(NHCDOWN);
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()));
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;
187 config = getConfig().as(NikoHomeControlActionConfig.class);
189 actionId = config.actionId;
191 NikoHomeControlCommunication nhcComm = getCommunication();
192 if (nhcComm == null) {
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 "
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);
212 nhcAction.setEventHandler(this);
216 String actionLocation = nhcAction.getLocation();
217 if (thing.getLocation() == null) {
218 thing.setLocation(actionLocation);
221 actionEvent(nhcAction.getState());
223 logger.debug("Niko Home Control: action initialized {}", actionId);
225 Bridge bridge = getBridge();
226 if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
227 updateStatus(ThingStatus.ONLINE);
229 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
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()));
242 if (nhcAction instanceof NhcAction2) {
243 NhcAction2 action = (NhcAction2) nhcAction;
244 properties.put("model", action.getModel());
245 properties.put("technology", action.getTechnology());
248 thing.setProperties(properties);
252 public void actionEvent(int actionState) {
253 ActionType actionType = nhcAction.getType();
255 switch (actionType) {
257 updateState(CHANNEL_BUTTON, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
258 updateStatus(ThingStatus.ONLINE);
260 updateState(CHANNEL_SWITCH, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
261 updateStatus(ThingStatus.ONLINE);
264 updateState(CHANNEL_BRIGHTNESS, new PercentType(actionState));
265 updateStatus(ThingStatus.ONLINE);
268 updateState(CHANNEL_ROLLERSHUTTER, new PercentType(actionState));
269 updateStatus(ThingStatus.ONLINE);
272 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
273 "Niko Home Control: unknown action type " + actionType);
278 public void actionRemoved() {
279 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
280 "Niko Home Control: action has been removed from the controller " + actionId);
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");
293 // Also put the bridge back online
294 NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
295 if (nhcBridgeHandler != null) {
296 nhcBridgeHandler.bridgeOnline();
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);
307 NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
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);
318 NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();
319 return nhcBridgeHandler;