2 * Copyright (c) 2010-2021 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 @Nullable NhcAction nhcAction;
57 private String actionId = "";
58 private int stepValue;
59 private boolean invert;
61 public NikoHomeControlActionHandler(Thing thing) {
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);
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 NhcAction nhcAction = this.nhcAction;
88 if (nhcAction == null) {
89 logger.debug("action with ID {} not initialized", actionId);
93 logger.debug("handle command {} for {}", command, channelUID);
95 if (command == REFRESH) {
96 actionEvent(nhcAction.getState());
100 switch (channelUID.getId()) {
103 handleSwitchCommand(command);
104 updateStatus(ThingStatus.ONLINE);
106 case CHANNEL_BRIGHTNESS:
107 handleBrightnessCommand(command);
108 updateStatus(ThingStatus.ONLINE);
111 case CHANNEL_ROLLERSHUTTER:
112 handleRollershutterCommand(command);
113 updateStatus(ThingStatus.ONLINE);
117 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
118 "Channel unknown " + channelUID.getId());
122 private void handleSwitchCommand(Command command) {
123 NhcAction nhcAction = this.nhcAction;
124 if (nhcAction == null) {
125 logger.debug("action with ID {} not initialized", actionId);
129 if (command instanceof OnOffType) {
130 OnOffType s = (OnOffType) command;
131 if (OnOffType.OFF.equals(s)) {
132 nhcAction.execute(NHCOFF);
134 nhcAction.execute(NHCON);
139 private void handleBrightnessCommand(Command command) {
140 NhcAction nhcAction = this.nhcAction;
141 if (nhcAction == null) {
142 logger.debug("action with ID {} not initialized", actionId);
146 if (command instanceof OnOffType) {
147 OnOffType s = (OnOffType) command;
148 if (OnOffType.OFF.equals(s)) {
149 nhcAction.execute(NHCOFF);
151 nhcAction.execute(NHCON);
153 } else if (command instanceof IncreaseDecreaseType) {
154 IncreaseDecreaseType s = (IncreaseDecreaseType) command;
155 int currentValue = nhcAction.getState();
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));
163 newValue = currentValue - stepValue;
164 // round up to step multiple
165 newValue = newValue + newValue % stepValue;
167 nhcAction.execute(NHCOFF);
169 nhcAction.execute(Integer.toString(newValue));
172 } else if (command instanceof PercentType) {
173 PercentType p = (PercentType) command;
174 if (PercentType.ZERO.equals(p)) {
175 nhcAction.execute(NHCOFF);
177 nhcAction.execute(Integer.toString(p.intValue()));
182 private void handleRollershutterCommand(Command command) {
183 NhcAction nhcAction = this.nhcAction;
184 if (nhcAction == null) {
185 logger.debug("action with ID {} not initialized", actionId);
189 if (command instanceof UpDownType) {
190 UpDownType s = (UpDownType) command;
191 if (UpDownType.UP.equals(s)) {
192 nhcAction.execute(!invert ? NHCUP : NHCDOWN);
194 nhcAction.execute(!invert ? NHCDOWN : NHCUP);
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()));
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;
214 config = getConfig().as(NikoHomeControlActionConfig.class);
216 actionId = config.actionId;
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);
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);
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");
240 nhcAction.setEventHandler(this);
244 String actionLocation = nhcAction.getLocation();
245 if (thing.getLocation() == null) {
246 thing.setLocation(actionLocation);
249 actionEvent(nhcAction.getState());
251 this.nhcAction = nhcAction;
253 logger.debug("action initialized {}", actionId);
255 Bridge bridge = getBridge();
256 if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
257 updateStatus(ThingStatus.ONLINE);
259 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
264 private void updateProperties() {
265 NhcAction nhcAction = this.nhcAction;
266 if (nhcAction == null) {
267 logger.debug("action with ID {} not initialized", actionId);
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()));
278 if (nhcAction instanceof NhcAction2) {
279 NhcAction2 action = (NhcAction2) nhcAction;
280 properties.put("model", action.getModel());
281 properties.put("technology", action.getTechnology());
284 thing.setProperties(properties);
288 public void actionEvent(int actionState) {
289 NhcAction nhcAction = this.nhcAction;
290 if (nhcAction == null) {
291 logger.debug("action with ID {} not initialized", actionId);
295 ActionType actionType = nhcAction.getType();
297 switch (actionType) {
299 updateState(CHANNEL_BUTTON, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
300 updateStatus(ThingStatus.ONLINE);
302 updateState(CHANNEL_SWITCH, (actionState == 0) ? OnOffType.OFF : OnOffType.ON);
303 updateStatus(ThingStatus.ONLINE);
306 updateState(CHANNEL_BRIGHTNESS, new PercentType(actionState));
307 updateStatus(ThingStatus.ONLINE);
310 updateState(CHANNEL_ROLLERSHUTTER,
311 !invert ? new PercentType(100 - actionState) : new PercentType(actionState));
312 updateStatus(ThingStatus.ONLINE);
315 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
316 "Unknown action type " + actionType);
321 public void actionInitialized() {
322 Bridge bridge = getBridge();
323 if ((bridge != null) && (bridge.getStatus() == ThingStatus.ONLINE)) {
324 updateStatus(ThingStatus.ONLINE);
329 public void actionRemoved() {
330 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
331 "Action " + actionId + " has been removed from the controller");
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");
343 // Also put the bridge back online
344 NikoHomeControlBridgeHandler nhcBridgeHandler = getBridgeHandler();
345 if (nhcBridgeHandler != null) {
346 nhcBridgeHandler.bridgeOnline();
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);
357 NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication();
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);
368 NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler();
369 return nhcBridgeHandler;