added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.nuki-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-nuki" description="Nuki Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-transport-http</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.nuki/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link NukiBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
* @contributer Christian Hoefler - Door sensor integration
|
||||
*/
|
||||
public class NukiBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "nuki";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
|
||||
public static final ThingTypeUID THING_TYPE_SMARTLOCK = new ThingTypeUID(BINDING_ID, "smartlock");
|
||||
|
||||
public static final Set<ThingTypeUID> THING_TYPE_BRIDGE_UIDS = Collections.singleton(THING_TYPE_BRIDGE);
|
||||
public static final Set<ThingTypeUID> THING_TYPE_SMARTLOCK_UIDS = Collections.singleton(THING_TYPE_SMARTLOCK);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
|
||||
.concat(THING_TYPE_BRIDGE_UIDS.stream(), THING_TYPE_SMARTLOCK_UIDS.stream()).collect(Collectors.toSet());
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_SMARTLOCK_LOCK = "lock";
|
||||
public static final String CHANNEL_SMARTLOCK_STATE = "lockState";
|
||||
public static final String CHANNEL_SMARTLOCK_LOW_BATTERY = "lowBattery";
|
||||
public static final String CHANNEL_SMARTLOCK_DOOR_STATE = "doorsensorState";
|
||||
|
||||
// List of all config-description parameters
|
||||
public static final String CONFIG_IP = "ip";
|
||||
public static final String CONFIG_PORT = "port";
|
||||
public static final String CONFIG_MANAGECB = "manageCallbacks";
|
||||
public static final String CONFIG_API_TOKEN = "apiToken";
|
||||
public static final String CONFIG_NUKI_ID = "nukiId";
|
||||
public static final String CONFIG_UNLATCH = "unlatch";
|
||||
|
||||
// Nuki Bridge API REST Endpoints
|
||||
public static final String URI_INFO = "http://%s:%s/info?token=%s";
|
||||
public static final String URI_LOCKSTATE = "http://%s:%s/lockState?token=%s&nukiId=%s";
|
||||
public static final String URI_LOCKACTION = "http://%s:%s/lockAction?token=%s&nukiId=%s&action=%s";
|
||||
public static final String URI_CBADD = "http://%s:%s/callback/add?token=%s&url=%s";
|
||||
public static final String URI_CBLIST = "http://%s:%s/callback/list?token=%s";
|
||||
public static final String URI_CBREMOVE = "http://%s:%s/callback/remove?token=%s&id=%s";
|
||||
|
||||
// openHAB Callback Endpoint & Nuki Bridge Callback URL
|
||||
public static final String CALLBACK_ENDPOINT = "/nuki/bcb";
|
||||
public static final String CALLBACK_URL = "http://%s" + CALLBACK_ENDPOINT;
|
||||
|
||||
// Nuki Bridge API Lock Actions
|
||||
public static final int LOCK_ACTIONS_UNLOCK = 1;
|
||||
public static final int LOCK_ACTIONS_LOCK = 2;
|
||||
public static final int LOCK_ACTIONS_UNLATCH = 3;
|
||||
public static final int LOCK_ACTIONS_LOCKNGO_UNLOCK = 4;
|
||||
public static final int LOCK_ACTIONS_LOCKNGO_UNLATCH = 5;
|
||||
|
||||
// Nuki Bridge API Lock States
|
||||
public static final int LOCK_STATES_UNCALIBRATED = 0;
|
||||
public static final int LOCK_STATES_LOCKED = 1;
|
||||
public static final int LOCK_STATES_UNLOCKING = 2;
|
||||
public static final int LOCK_STATES_UNLOCKED = 3;
|
||||
public static final int LOCK_STATES_LOCKING = 4;
|
||||
public static final int LOCK_STATES_UNLATCHED = 5;
|
||||
public static final int LOCK_STATES_UNLOCKED_LOCKNGO = 6;
|
||||
public static final int LOCK_STATES_UNLATCHING = 7;
|
||||
public static final int LOCK_STATES_MOTOR_BLOCKED = 254;
|
||||
public static final int LOCK_STATES_UNDEFINED = 255;
|
||||
|
||||
// Nuki Binding additional Lock States
|
||||
public static final int LOCK_STATES_UNLOCKING_LOCKNGO = 1002;
|
||||
public static final int LOCK_STATES_UNLATCHING_LOCKNGO = 1007;
|
||||
|
||||
// Nuki Binding Door States
|
||||
public static final int DOORSENSOR_STATES_UNAVAILABLE = 0;
|
||||
public static final int DOORSENSOR_STATES_DEACTIVATED = 1;
|
||||
public static final int DOORSENSOR_STATES_CLOSED = 2;
|
||||
public static final int DOORSENSOR_STATES_OPEN = 3;
|
||||
public static final int DOORSENSOR_STATES_UNKNOWN = 4;
|
||||
public static final int DOORSENSOR_STATES_CALIBRATING = 5;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.NukiApiServlet;
|
||||
import org.openhab.binding.nuki.internal.handler.NukiBridgeHandler;
|
||||
import org.openhab.binding.nuki.internal.handler.NukiSmartLockHandler;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.net.HttpServiceUtil;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.http.HttpService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link NukiHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.nuki")
|
||||
@NonNullByDefault
|
||||
public class NukiHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(NukiHandlerFactory.class);
|
||||
|
||||
private final HttpService httpService;
|
||||
private final HttpClient httpClient;
|
||||
private final NetworkAddressService networkAddressService;
|
||||
private @Nullable String callbackUrl;
|
||||
private @Nullable NukiApiServlet nukiApiServlet;
|
||||
|
||||
@Activate
|
||||
public NukiHandlerFactory(@Reference HttpService httpService, @Reference final HttpClientFactory httpClientFactory,
|
||||
@Reference NetworkAddressService networkAddressService) {
|
||||
this.httpService = httpService;
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.networkAddressService = networkAddressService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return NukiBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
logger.debug("NukiHandlerFactory:createHandler({})", thing);
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (NukiBindingConstants.THING_TYPE_BRIDGE_UIDS.contains(thingTypeUID)) {
|
||||
callbackUrl = createCallbackUrl();
|
||||
NukiBridgeHandler nukiBridgeHandler = new NukiBridgeHandler((Bridge) thing, httpClient, callbackUrl);
|
||||
if (!nukiBridgeHandler.isInitializable()) {
|
||||
return null;
|
||||
}
|
||||
if (nukiApiServlet == null) {
|
||||
nukiApiServlet = new NukiApiServlet(httpService);
|
||||
}
|
||||
nukiApiServlet.add(nukiBridgeHandler);
|
||||
return nukiBridgeHandler;
|
||||
} else if (NukiBindingConstants.THING_TYPE_SMARTLOCK_UIDS.contains(thingTypeUID)) {
|
||||
return new NukiSmartLockHandler(thing);
|
||||
}
|
||||
logger.trace("No valid Handler found for Thing[{}]!", thingTypeUID);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterHandler(Thing thing) {
|
||||
super.unregisterHandler(thing);
|
||||
logger.trace("NukiHandlerFactory:unregisterHandler({})", thing);
|
||||
if (thing.getHandler() instanceof NukiBridgeHandler && nukiApiServlet != null) {
|
||||
nukiApiServlet.remove((NukiBridgeHandler) thing.getHandler());
|
||||
if (nukiApiServlet.countNukiBridgeHandlers() == 0) {
|
||||
nukiApiServlet = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable String createCallbackUrl() {
|
||||
logger.trace("createCallbackUrl()");
|
||||
if (callbackUrl != null) {
|
||||
return callbackUrl;
|
||||
}
|
||||
final String ipAddress = networkAddressService.getPrimaryIpv4HostAddress();
|
||||
if (ipAddress == null) {
|
||||
logger.warn("No network interface could be found.");
|
||||
return null;
|
||||
}
|
||||
// we do not use SSL as it can cause certificate validation issues.
|
||||
final int port = HttpServiceUtil.getHttpServicePort(bundleContext);
|
||||
if (port == -1) {
|
||||
logger.warn("Cannot find port of the http service.");
|
||||
return null;
|
||||
}
|
||||
ArrayList<String> parameters = new ArrayList<>();
|
||||
parameters.add(ipAddress + ":" + port);
|
||||
String callbackUrl = String.format(NukiBindingConstants.CALLBACK_URL, parameters.toArray());
|
||||
logger.trace("callbackUrl[{}]", callbackUrl);
|
||||
return callbackUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.converter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.openhab.binding.nuki.internal.NukiBindingConstants;
|
||||
|
||||
/**
|
||||
* The {@link LockActionConverter} is responsible for mapping Binding Lock States to Bridge HTTP-API Lock Actions.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public abstract class LockActionConverter {
|
||||
|
||||
private static Map<Integer, Integer> mapping;
|
||||
|
||||
private static void setupMapping() {
|
||||
mapping = new HashMap<>();
|
||||
mapping.put(NukiBindingConstants.LOCK_STATES_UNLOCKING, NukiBindingConstants.LOCK_ACTIONS_UNLOCK);
|
||||
mapping.put(NukiBindingConstants.LOCK_STATES_LOCKING, NukiBindingConstants.LOCK_ACTIONS_LOCK);
|
||||
mapping.put(NukiBindingConstants.LOCK_STATES_UNLATCHING, NukiBindingConstants.LOCK_ACTIONS_UNLATCH);
|
||||
mapping.put(NukiBindingConstants.LOCK_STATES_UNLOCKING_LOCKNGO,
|
||||
NukiBindingConstants.LOCK_ACTIONS_LOCKNGO_UNLOCK);
|
||||
mapping.put(NukiBindingConstants.LOCK_STATES_UNLATCHING_LOCKNGO,
|
||||
NukiBindingConstants.LOCK_ACTIONS_LOCKNGO_UNLATCH);
|
||||
}
|
||||
|
||||
public static int getLockActionFor(int lockState) {
|
||||
if (mapping == null) {
|
||||
setupMapping();
|
||||
}
|
||||
return mapping.get(lockState);
|
||||
}
|
||||
|
||||
public static int getLockStateFor(int lockAction) {
|
||||
if (mapping == null) {
|
||||
setupMapping();
|
||||
}
|
||||
for (Map.Entry<Integer, Integer> entry : mapping.entrySet()) {
|
||||
if (entry.getValue() == lockAction) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dataexchange;
|
||||
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackAddDto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeCallbackAddResponse} class wraps {@link BridgeApiCallbackAddDto} class.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeCallbackAddResponse extends NukiBaseResponse {
|
||||
|
||||
public BridgeCallbackAddResponse(int status, String message, BridgeApiCallbackAddDto bridgeApiCallbackAddDto) {
|
||||
super(status, message);
|
||||
if (bridgeApiCallbackAddDto != null) {
|
||||
this.setSuccess(bridgeApiCallbackAddDto.isSuccess());
|
||||
if (bridgeApiCallbackAddDto.getMessage() != null) {
|
||||
this.setMessage(bridgeApiCallbackAddDto.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeCallbackAddResponse(NukiBaseResponse nukiBaseResponse) {
|
||||
super(nukiBaseResponse.getStatus(), nukiBaseResponse.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dataexchange;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackListCallbackDto;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackListDto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeCallbackListResponse} class wraps {@link BridgeApiCallbackListDto} class.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeCallbackListResponse extends NukiBaseResponse {
|
||||
|
||||
private List<BridgeApiCallbackListCallbackDto> callbacks;
|
||||
|
||||
public BridgeCallbackListResponse(int status, String message, BridgeApiCallbackListDto bridgeApiCallbackListDto) {
|
||||
super(status, message);
|
||||
if (bridgeApiCallbackListDto != null) {
|
||||
this.setSuccess(true);
|
||||
this.callbacks = bridgeApiCallbackListDto.getCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeCallbackListResponse(NukiBaseResponse nukiBaseResponse) {
|
||||
super(nukiBaseResponse.getStatus(), nukiBaseResponse.getMessage());
|
||||
}
|
||||
|
||||
public List<BridgeApiCallbackListCallbackDto> getCallbacks() {
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
public void setCallbacks(List<BridgeApiCallbackListCallbackDto> callbacks) {
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dataexchange;
|
||||
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackRemoveDto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeCallbackRemoveResponse} class wraps {@link BridgeApiCallbackRemoveDto} class.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeCallbackRemoveResponse extends NukiBaseResponse {
|
||||
|
||||
public BridgeCallbackRemoveResponse(int status, String message,
|
||||
BridgeApiCallbackRemoveDto bridgeApiCallbackRemoveDto) {
|
||||
super(status, message);
|
||||
if (bridgeApiCallbackRemoveDto != null) {
|
||||
this.setSuccess(bridgeApiCallbackRemoveDto.isSuccess());
|
||||
if (bridgeApiCallbackRemoveDto.getMessage() != null) {
|
||||
this.setMessage(bridgeApiCallbackRemoveDto.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeCallbackRemoveResponse(NukiBaseResponse nukiBaseResponse) {
|
||||
super(nukiBaseResponse.getStatus(), nukiBaseResponse.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dataexchange;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiInfoDto;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiInfoScanResultDto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeInfoResponse} class wraps {@link BridgeApiInfoDto} class.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeInfoResponse extends NukiBaseResponse {
|
||||
|
||||
private int bridgeType;
|
||||
private int hardwareId;
|
||||
private int serverId;
|
||||
private String firmwareVersion;
|
||||
private String wifiFirmwareVersion;
|
||||
private int uptime;
|
||||
private String currentTime;
|
||||
private boolean serverConnected;
|
||||
private List<BridgeApiInfoScanResultDto> scanResults;
|
||||
|
||||
public BridgeInfoResponse(int status, String message, BridgeApiInfoDto bridgeApiInfoDto) {
|
||||
super(status, message);
|
||||
if (bridgeApiInfoDto != null) {
|
||||
this.setSuccess(true);
|
||||
this.setBridgeType(bridgeApiInfoDto.getBridgeType());
|
||||
this.setHardwareId(bridgeApiInfoDto.getIds().getHardwareId());
|
||||
this.setServerId(bridgeApiInfoDto.getIds().getServerId());
|
||||
this.setFirmwareVersion(bridgeApiInfoDto.getVersions().getFirmwareVersion());
|
||||
this.setWifiFirmwareVersion(bridgeApiInfoDto.getVersions().getWifiFirmwareVersion());
|
||||
this.setUptime(bridgeApiInfoDto.getUptime());
|
||||
this.setCurrentTime(bridgeApiInfoDto.getCurrentTime());
|
||||
this.setScanResults(bridgeApiInfoDto.getScanResults());
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeInfoResponse(NukiBaseResponse nukiBaseResponse) {
|
||||
super(nukiBaseResponse.getStatus(), nukiBaseResponse.getMessage());
|
||||
}
|
||||
|
||||
public int getBridgeType() {
|
||||
return bridgeType;
|
||||
}
|
||||
|
||||
public void setBridgeType(int bridgeType) {
|
||||
this.bridgeType = bridgeType;
|
||||
}
|
||||
|
||||
public int getHardwareId() {
|
||||
return hardwareId;
|
||||
}
|
||||
|
||||
public void setHardwareId(int hardwareId) {
|
||||
this.hardwareId = hardwareId;
|
||||
}
|
||||
|
||||
public int getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(int serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
|
||||
public String getFirmwareVersion() {
|
||||
return firmwareVersion;
|
||||
}
|
||||
|
||||
public void setFirmwareVersion(String firmwareVersion) {
|
||||
this.firmwareVersion = firmwareVersion;
|
||||
}
|
||||
|
||||
public String getWifiFirmwareVersion() {
|
||||
return wifiFirmwareVersion;
|
||||
}
|
||||
|
||||
public void setWifiFirmwareVersion(String wifiFirmwareVersion) {
|
||||
this.wifiFirmwareVersion = wifiFirmwareVersion;
|
||||
}
|
||||
|
||||
public int getUptime() {
|
||||
return uptime;
|
||||
}
|
||||
|
||||
public void setUptime(int uptime) {
|
||||
this.uptime = uptime;
|
||||
}
|
||||
|
||||
public String getCurrentTime() {
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
public void setCurrentTime(String currentTime) {
|
||||
this.currentTime = currentTime;
|
||||
}
|
||||
|
||||
public boolean isServerConnected() {
|
||||
return serverConnected;
|
||||
}
|
||||
|
||||
public void setServerConnected(boolean serverConnected) {
|
||||
this.serverConnected = serverConnected;
|
||||
}
|
||||
|
||||
public List<BridgeApiInfoScanResultDto> getScanResults() {
|
||||
return scanResults;
|
||||
}
|
||||
|
||||
public void setScanResults(List<BridgeApiInfoScanResultDto> scanResults) {
|
||||
this.scanResults = scanResults;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dataexchange;
|
||||
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiLockActionDto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeLockActionResponse} class wraps {@link BridgeApiLockActionDto} class.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeLockActionResponse extends NukiBaseResponse {
|
||||
|
||||
private boolean batteryCritical;
|
||||
|
||||
public BridgeLockActionResponse(int status, String message, BridgeApiLockActionDto bridgeApiLockActionDto) {
|
||||
super(status, message);
|
||||
if (bridgeApiLockActionDto != null) {
|
||||
this.setSuccess(bridgeApiLockActionDto.isSuccess());
|
||||
this.setBatteryCritical(bridgeApiLockActionDto.isBatteryCritical());
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeLockActionResponse(NukiBaseResponse nukiBaseResponse) {
|
||||
super(nukiBaseResponse.getStatus(), nukiBaseResponse.getMessage());
|
||||
}
|
||||
|
||||
public boolean isBatteryCritical() {
|
||||
return batteryCritical;
|
||||
}
|
||||
|
||||
public void setBatteryCritical(boolean batteryCritical) {
|
||||
this.batteryCritical = batteryCritical;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dataexchange;
|
||||
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiLockStateDto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeLockStateResponse} class wraps {@link BridgeApiLockStateDto} class.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
* @contributer Christian Hoefler - Door sensor integration
|
||||
*/
|
||||
public class BridgeLockStateResponse extends NukiBaseResponse {
|
||||
|
||||
private int state;
|
||||
private String stateName;
|
||||
private boolean batteryCritical;
|
||||
private int doorsensorState;
|
||||
|
||||
public BridgeLockStateResponse(int status, String message, BridgeApiLockStateDto bridgeApiLockStateDto) {
|
||||
super(status, message);
|
||||
if (bridgeApiLockStateDto != null) {
|
||||
this.setSuccess(bridgeApiLockStateDto.isSuccess());
|
||||
this.setState(bridgeApiLockStateDto.getState());
|
||||
this.setStateName(bridgeApiLockStateDto.getStateName());
|
||||
this.setDoorsensorState(bridgeApiLockStateDto.getDoorsensorState());
|
||||
this.setBatteryCritical(bridgeApiLockStateDto.isBatteryCritical());
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeLockStateResponse(NukiBaseResponse nukiBaseResponse) {
|
||||
super(nukiBaseResponse.getStatus(), nukiBaseResponse.getMessage());
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getStateName() {
|
||||
return stateName;
|
||||
}
|
||||
|
||||
public void setStateName(String stateName) {
|
||||
this.stateName = stateName;
|
||||
}
|
||||
|
||||
public boolean isBatteryCritical() {
|
||||
return batteryCritical;
|
||||
}
|
||||
|
||||
public void setBatteryCritical(boolean batteryCritical) {
|
||||
this.batteryCritical = batteryCritical;
|
||||
}
|
||||
|
||||
public int getDoorsensorState() {
|
||||
return doorsensorState;
|
||||
}
|
||||
|
||||
public void setDoorsensorState(int doorsensorState) {
|
||||
this.doorsensorState = doorsensorState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dataexchange;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Dictionary;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.nuki.internal.NukiBindingConstants;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiLockStateRequestDto;
|
||||
import org.openhab.binding.nuki.internal.dto.NukiHttpServerStatusResponseDto;
|
||||
import org.openhab.binding.nuki.internal.handler.NukiBridgeHandler;
|
||||
import org.openhab.binding.nuki.internal.handler.NukiSmartLockHandler;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.State;
|
||||
import org.osgi.service.http.HttpService;
|
||||
import org.osgi.service.http.NamespaceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link NukiApiServlet} class is responsible for handling the callbacks from the Nuki Bridge.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
* @contributer Christian Hoefler - Door sensor integration
|
||||
*/
|
||||
public class NukiApiServlet extends HttpServlet {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(NukiApiServlet.class);
|
||||
private static final long serialVersionUID = -3601163473320027239L;
|
||||
private static final String CHARSET = "utf-8";
|
||||
private static final String APPLICATION_JSON = "application/json";
|
||||
|
||||
private HttpService httpService;
|
||||
private List<NukiBridgeHandler> nukiBridgeHandlers = new ArrayList<>();
|
||||
private String path;
|
||||
private Gson gson;
|
||||
|
||||
public NukiApiServlet(HttpService httpService) {
|
||||
logger.debug("Instantiating NukiApiServlet({})", httpService);
|
||||
this.httpService = httpService;
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
public void add(@NonNull NukiBridgeHandler nukiBridgeHandler) {
|
||||
logger.trace("Adding NukiBridgeHandler[{}] for Bridge[{}] to NukiApiServlet.",
|
||||
nukiBridgeHandler.getThing().getUID(),
|
||||
nukiBridgeHandler.getThing().getConfiguration().get(NukiBindingConstants.CONFIG_IP));
|
||||
if (!nukiBridgeHandler.isInitializable()) {
|
||||
logger.debug("NukiBridgeHandler[{}] is not initializable, check required configuration!",
|
||||
nukiBridgeHandler.getThing().getUID());
|
||||
return;
|
||||
}
|
||||
if (nukiBridgeHandlers.isEmpty()) {
|
||||
this.activate();
|
||||
}
|
||||
nukiBridgeHandlers.add(nukiBridgeHandler);
|
||||
}
|
||||
|
||||
public void remove(NukiBridgeHandler nukiBridgeHandler) {
|
||||
logger.trace("Removing NukiBridgeHandler[{}] for Bridge[{}] from NukiApiServlet.",
|
||||
nukiBridgeHandler.getThing().getUID(),
|
||||
nukiBridgeHandler.getThing().getConfiguration().get(NukiBindingConstants.CONFIG_IP));
|
||||
nukiBridgeHandlers.remove(nukiBridgeHandler);
|
||||
if (nukiBridgeHandlers.isEmpty()) {
|
||||
this.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
public int countNukiBridgeHandlers() {
|
||||
return nukiBridgeHandlers.size();
|
||||
}
|
||||
|
||||
private void activate() {
|
||||
logger.debug("Activating NukiApiServlet.");
|
||||
path = NukiBindingConstants.CALLBACK_ENDPOINT;
|
||||
Dictionary<String, String> servletParams = new Hashtable<>();
|
||||
try {
|
||||
httpService.registerServlet(path, this, servletParams, httpService.createDefaultHttpContext());
|
||||
logger.debug("Started NukiApiServlet at path[{}]", path);
|
||||
} catch (ServletException | NamespaceException e) {
|
||||
logger.error("ERROR: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deactivate() {
|
||||
logger.trace("deactivate()");
|
||||
httpService.unregister(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
logger.debug("Servlet Request at URI[{}] request[{}]", request.getRequestURI(), request);
|
||||
BridgeApiLockStateRequestDto bridgeApiLockStateRequestDto = getBridgeApiLockStateRequestDto(request);
|
||||
if (bridgeApiLockStateRequestDto == null) {
|
||||
logger.error("Could not handle Bridge CallBack Request - Discarding!");
|
||||
logger.error("Please report a bug, if this request was done by the Nuki Bridge!");
|
||||
setHeaders(response);
|
||||
response.setStatus(HttpStatus.BAD_REQUEST_400);
|
||||
response.getWriter().println(gson.toJson(new NukiHttpServerStatusResponseDto("Invalid BCB-Request!")));
|
||||
return;
|
||||
}
|
||||
String nukiId = String.format("%08X", (bridgeApiLockStateRequestDto.getNukiId()));
|
||||
String nukiIdThing;
|
||||
for (NukiBridgeHandler nukiBridgeHandler : nukiBridgeHandlers) {
|
||||
logger.trace("Searching Bridge[{}] with NukiBridgeHandler[{}] for nukiId[{}].",
|
||||
nukiBridgeHandler.getThing().getConfiguration().get(NukiBindingConstants.CONFIG_IP),
|
||||
nukiBridgeHandler.getThing().getUID(), nukiId);
|
||||
List<@NonNull Thing> allSmartLocks = nukiBridgeHandler.getThing().getThings();
|
||||
for (Thing thing : allSmartLocks) {
|
||||
nukiIdThing = thing.getConfiguration().containsKey(NukiBindingConstants.CONFIG_NUKI_ID)
|
||||
? (String) thing.getConfiguration().get(NukiBindingConstants.CONFIG_NUKI_ID)
|
||||
: null;
|
||||
if (nukiIdThing != null && nukiIdThing.equals(nukiId)) {
|
||||
logger.debug("Processing ThingUID[{}] - nukiId[{}]", thing.getUID(), nukiId);
|
||||
NukiSmartLockHandler nsh = getSmartLockHandler(thing);
|
||||
if (nsh == null) {
|
||||
logger.debug("Could not update channels for ThingUID[{}] because Handler is null!",
|
||||
thing.getUID());
|
||||
break;
|
||||
}
|
||||
Channel channel = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_LOCK);
|
||||
if (channel != null) {
|
||||
State state = bridgeApiLockStateRequestDto.getState() == NukiBindingConstants.LOCK_STATES_LOCKED
|
||||
? OnOffType.ON
|
||||
: OnOffType.OFF;
|
||||
nsh.handleApiServletUpdate(channel.getUID(), state);
|
||||
}
|
||||
channel = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_STATE);
|
||||
if (channel != null) {
|
||||
State state = new DecimalType(bridgeApiLockStateRequestDto.getState());
|
||||
nsh.handleApiServletUpdate(channel.getUID(), state);
|
||||
}
|
||||
channel = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_LOW_BATTERY);
|
||||
if (channel != null) {
|
||||
State state = bridgeApiLockStateRequestDto.isBatteryCritical() ? OnOffType.ON : OnOffType.OFF;
|
||||
nsh.handleApiServletUpdate(channel.getUID(), state);
|
||||
}
|
||||
channel = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_DOOR_STATE);
|
||||
if (channel != null) {
|
||||
State state = new DecimalType(bridgeApiLockStateRequestDto.getDoorsensorState());
|
||||
nsh.handleApiServletUpdate(channel.getUID(), state);
|
||||
}
|
||||
setHeaders(response);
|
||||
response.getWriter().println(gson.toJson(new NukiHttpServerStatusResponseDto("OK")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug("Smart Lock with nukiId[{}] not found!", nukiId);
|
||||
setHeaders(response);
|
||||
response.setStatus(HttpStatus.NOT_FOUND_404);
|
||||
response.getWriter().println(gson.toJson(new NukiHttpServerStatusResponseDto("Smart Lock not found!")));
|
||||
}
|
||||
|
||||
private BridgeApiLockStateRequestDto getBridgeApiLockStateRequestDto(HttpServletRequest request) {
|
||||
logger.trace("getBridgeApiLockStateRequestDto(...)");
|
||||
String requestContent = null;
|
||||
try {
|
||||
requestContent = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
|
||||
BridgeApiLockStateRequestDto bridgeApiLockStateRequestDto = gson.fromJson(requestContent,
|
||||
BridgeApiLockStateRequestDto.class);
|
||||
if (bridgeApiLockStateRequestDto.getNukiId() != 0) {
|
||||
logger.trace("requestContent[{}]", requestContent);
|
||||
return bridgeApiLockStateRequestDto;
|
||||
} else {
|
||||
logger.error("Invalid BCB-Request payload data!");
|
||||
logger.error("requestContent[{}]", requestContent);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not read payload from BCB-Request! Message[{}]", e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("Could not create BridgeApiLockStateRequestDto from BCB-Request! Message[{}]", e.getMessage());
|
||||
logger.error("requestContent[{}]", requestContent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private NukiSmartLockHandler getSmartLockHandler(Thing thing) {
|
||||
logger.trace("getSmartLockHandler(...) from thing[{}]", thing.getUID());
|
||||
NukiSmartLockHandler nsh = (NukiSmartLockHandler) thing.getHandler();
|
||||
if (nsh == null) {
|
||||
logger.debug("Could not get NukiSmartLockHandler for ThingUID[{}]!", thing.getUID());
|
||||
return null;
|
||||
}
|
||||
return nsh;
|
||||
}
|
||||
|
||||
private void setHeaders(HttpServletResponse response) {
|
||||
response.setCharacterEncoding(CHARSET);
|
||||
response.setContentType(APPLICATION_JSON);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dataexchange;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* The {@link NukiBaseResponse} class is the base class for API Responses.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class NukiBaseResponse {
|
||||
|
||||
private int status;
|
||||
private String message;
|
||||
private boolean success;
|
||||
private Instant created;
|
||||
|
||||
public NukiBaseResponse(int status, String message) {
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
this.created = Instant.now();
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public Instant getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dataexchange;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.SocketException;
|
||||
import java.net.URLEncoder;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.HttpResponseException;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.nuki.internal.NukiBindingConstants;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackAddDto;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackListDto;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackRemoveDto;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiInfoDto;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiLockActionDto;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiLockStateDto;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link NukiHttpClient} class is responsible for getting data from the Nuki Bridge.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class NukiHttpClient {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(NukiHttpClient.class);
|
||||
private static final long CACHE_PERIOD = 5;
|
||||
|
||||
private HttpClient httpClient;
|
||||
private Configuration configuration;
|
||||
private Gson gson;
|
||||
private BridgeLockStateResponse bridgeLockStateResponseCache;
|
||||
|
||||
public NukiHttpClient(HttpClient httpClient, Configuration configuration) {
|
||||
logger.debug("Instantiating NukiHttpClient({})", configuration);
|
||||
this.configuration = configuration;
|
||||
this.httpClient = httpClient;
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
private String prepareUri(String uriTemplate, String... additionalArguments) {
|
||||
String configIp = (String) configuration.get(NukiBindingConstants.CONFIG_IP);
|
||||
BigDecimal configPort = (BigDecimal) configuration.get(NukiBindingConstants.CONFIG_PORT);
|
||||
String configApiToken = (String) configuration.get(NukiBindingConstants.CONFIG_API_TOKEN);
|
||||
ArrayList<String> parameters = new ArrayList<>();
|
||||
parameters.add(configIp);
|
||||
parameters.add(configPort.toString());
|
||||
parameters.add(configApiToken);
|
||||
if (additionalArguments != null) {
|
||||
for (String argument : additionalArguments) {
|
||||
parameters.add(argument);
|
||||
}
|
||||
}
|
||||
String uri = String.format(uriTemplate, parameters.toArray());
|
||||
logger.trace("prepareUri(...):URI[{}]", uri);
|
||||
return uri;
|
||||
}
|
||||
|
||||
private synchronized ContentResponse executeRequest(String uri)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
logger.debug("executeRequest({})", uri);
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
ContentResponse contentResponse = httpClient.GET(uri);
|
||||
logger.debug("contentResponseAsString[{}]", contentResponse.getContentAsString());
|
||||
return contentResponse;
|
||||
}
|
||||
|
||||
private NukiBaseResponse handleException(Exception e) {
|
||||
if (e instanceof ExecutionException) {
|
||||
if (e.getCause() instanceof HttpResponseException) {
|
||||
HttpResponseException cause = (HttpResponseException) e.getCause();
|
||||
int status = cause.getResponse().getStatus();
|
||||
String reason = cause.getResponse().getReason();
|
||||
logger.debug("HTTP Response Exception! Status[{}] - Reason[{}]! Check your API Token!", status, reason);
|
||||
return new NukiBaseResponse(status, reason);
|
||||
} else if (e.getCause() instanceof InterruptedIOException) {
|
||||
logger.debug(
|
||||
"InterruptedIOException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
|
||||
e.getMessage());
|
||||
return new NukiBaseResponse(HttpStatus.REQUEST_TIMEOUT_408,
|
||||
"InterruptedIOException! Check IP/Port configuration and if Nuki Bridge is powered on!");
|
||||
} else if (e.getCause() instanceof SocketException) {
|
||||
logger.debug(
|
||||
"SocketException! Exception[{}]! Check IP/Port configuration and if Nuki Bridge is powered on!",
|
||||
e.getMessage());
|
||||
return new NukiBaseResponse(HttpStatus.NOT_FOUND_404,
|
||||
"SocketException! Check IP/Port configuration and if Nuki Bridge is powered on!");
|
||||
}
|
||||
}
|
||||
logger.error("Could not handle Exception! Exception[{}]", e.getMessage(), e);
|
||||
return new NukiBaseResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, e.getMessage());
|
||||
}
|
||||
|
||||
public BridgeInfoResponse getBridgeInfo() {
|
||||
logger.debug("getBridgeInfo() in thread {}", Thread.currentThread().getId());
|
||||
String uri = prepareUri(NukiBindingConstants.URI_INFO);
|
||||
try {
|
||||
ContentResponse contentResponse = executeRequest(uri);
|
||||
int status = contentResponse.getStatus();
|
||||
String response = contentResponse.getContentAsString();
|
||||
logger.debug("getBridgeInfo status[{}] response[{}]", status, response);
|
||||
if (status == HttpStatus.OK_200) {
|
||||
BridgeApiInfoDto bridgeApiInfoDto = gson.fromJson(response, BridgeApiInfoDto.class);
|
||||
logger.debug("getBridgeInfo OK");
|
||||
return new BridgeInfoResponse(status, contentResponse.getReason(), bridgeApiInfoDto);
|
||||
} else {
|
||||
logger.debug("Could not get Bridge Info! Status[{}] - Response[{}]", status, response);
|
||||
return new BridgeInfoResponse(status, contentResponse.getReason(), null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Could not get Bridge Info! Exception[{}]", e.getMessage());
|
||||
return new BridgeInfoResponse(handleException(e));
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeLockStateResponse getBridgeLockState(String nukiId) {
|
||||
logger.debug("getBridgeLockState({}) in thread {}", nukiId, Thread.currentThread().getId());
|
||||
long timestampSecs = Instant.now().getEpochSecond();
|
||||
if (this.bridgeLockStateResponseCache != null
|
||||
&& timestampSecs < this.bridgeLockStateResponseCache.getCreated().getEpochSecond() + CACHE_PERIOD) {
|
||||
logger.debug("Returning LockState from cache - now[{}]<created[{}]+cachePeriod[{}]", timestampSecs,
|
||||
this.bridgeLockStateResponseCache.getCreated().getEpochSecond(), CACHE_PERIOD);
|
||||
return bridgeLockStateResponseCache;
|
||||
} else {
|
||||
logger.debug("Requesting LockState from Bridge.");
|
||||
}
|
||||
String uri = prepareUri(NukiBindingConstants.URI_LOCKSTATE, nukiId);
|
||||
try {
|
||||
ContentResponse contentResponse = executeRequest(uri);
|
||||
int status = contentResponse.getStatus();
|
||||
String response = contentResponse.getContentAsString();
|
||||
logger.debug("getBridgeLockState status[{}] response[{}]", status, response);
|
||||
if (status == HttpStatus.OK_200) {
|
||||
BridgeApiLockStateDto bridgeApiLockStateDto = gson.fromJson(response, BridgeApiLockStateDto.class);
|
||||
logger.debug("getBridgeLockState OK");
|
||||
return bridgeLockStateResponseCache = new BridgeLockStateResponse(status, contentResponse.getReason(),
|
||||
bridgeApiLockStateDto);
|
||||
} else {
|
||||
logger.debug("Could not get Lock State! Status[{}] - Response[{}]", status, response);
|
||||
return new BridgeLockStateResponse(status, contentResponse.getReason(), null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Could not get Bridge Lock State! Exception[{}]", e.getMessage());
|
||||
return new BridgeLockStateResponse(handleException(e));
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeLockActionResponse getBridgeLockAction(String nukiId, int lockAction) {
|
||||
logger.debug("getBridgeLockAction({}, {}) in thread {}", nukiId, lockAction, Thread.currentThread().getId());
|
||||
String uri = prepareUri(NukiBindingConstants.URI_LOCKACTION, nukiId, Integer.toString(lockAction));
|
||||
try {
|
||||
ContentResponse contentResponse = executeRequest(uri);
|
||||
int status = contentResponse.getStatus();
|
||||
String response = contentResponse.getContentAsString();
|
||||
logger.debug("getBridgeLockAction status[{}] response[{}]", status, response);
|
||||
if (status == HttpStatus.OK_200) {
|
||||
BridgeApiLockActionDto bridgeApiLockActionDto = gson.fromJson(response, BridgeApiLockActionDto.class);
|
||||
logger.debug("getBridgeLockAction OK");
|
||||
return new BridgeLockActionResponse(status, contentResponse.getReason(), bridgeApiLockActionDto);
|
||||
} else {
|
||||
logger.debug("Could not execute Lock Action! Status[{}] - Response[{}]", status, response);
|
||||
return new BridgeLockActionResponse(status, contentResponse.getReason(), null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Could not execute Lock Action! Exception[{}]", e.getMessage());
|
||||
return new BridgeLockActionResponse(handleException(e));
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeCallbackAddResponse getBridgeCallbackAdd(String callbackUrl) {
|
||||
logger.debug("getBridgeCallbackAdd({}) in thread {}", callbackUrl, Thread.currentThread().getId());
|
||||
try {
|
||||
String uri = prepareUri(NukiBindingConstants.URI_CBADD, URLEncoder.encode(callbackUrl, "UTF-8"));
|
||||
ContentResponse contentResponse = executeRequest(uri);
|
||||
int status = contentResponse.getStatus();
|
||||
String response = contentResponse.getContentAsString();
|
||||
logger.debug("getBridgeCallbackAdd status[{}] response[{}]", status, response);
|
||||
if (status == HttpStatus.OK_200) {
|
||||
BridgeApiCallbackAddDto bridgeApiCallbackAddDto = gson.fromJson(response,
|
||||
BridgeApiCallbackAddDto.class);
|
||||
logger.debug("getBridgeCallbackAdd OK");
|
||||
return new BridgeCallbackAddResponse(status, contentResponse.getReason(), bridgeApiCallbackAddDto);
|
||||
} else {
|
||||
logger.debug("Could not execute Callback Add! Status[{}] - Response[{}]", status, response);
|
||||
return new BridgeCallbackAddResponse(status, contentResponse.getReason(), null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Could not execute Callback Add! Exception[{}]", e.getMessage());
|
||||
return new BridgeCallbackAddResponse(handleException(e));
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeCallbackListResponse getBridgeCallbackList() {
|
||||
logger.debug("getBridgeCallbackList() in thread {}", Thread.currentThread().getId());
|
||||
String uri = prepareUri(NukiBindingConstants.URI_CBLIST);
|
||||
try {
|
||||
ContentResponse contentResponse = executeRequest(uri);
|
||||
int status = contentResponse.getStatus();
|
||||
String response = contentResponse.getContentAsString();
|
||||
logger.debug("getBridgeCallbackList status[{}] response[{}]", status, response);
|
||||
if (status == HttpStatus.OK_200) {
|
||||
BridgeApiCallbackListDto bridgeApiCallbackListDto = gson.fromJson(response,
|
||||
BridgeApiCallbackListDto.class);
|
||||
logger.debug("getBridgeCallbackList OK");
|
||||
return new BridgeCallbackListResponse(status, contentResponse.getReason(), bridgeApiCallbackListDto);
|
||||
} else {
|
||||
logger.debug("Could not execute Callback List! Status[{}] - Response[{}]", status, response);
|
||||
return new BridgeCallbackListResponse(status, contentResponse.getReason(), null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Could not execute Callback List! Exception[{}]", e.getMessage());
|
||||
return new BridgeCallbackListResponse(handleException(e));
|
||||
}
|
||||
}
|
||||
|
||||
public BridgeCallbackRemoveResponse getBridgeCallbackRemove(int id) {
|
||||
logger.debug("getBridgeCallbackRemove({}) in thread {}", id, Thread.currentThread().getId());
|
||||
try {
|
||||
String uri = prepareUri(NukiBindingConstants.URI_CBREMOVE, Integer.toString(id));
|
||||
ContentResponse contentResponse = executeRequest(uri);
|
||||
int status = contentResponse.getStatus();
|
||||
String response = contentResponse.getContentAsString();
|
||||
logger.debug("getBridgeCallbackRemove status[{}] response[{}]", status, response);
|
||||
if (status == HttpStatus.OK_200) {
|
||||
BridgeApiCallbackRemoveDto bridgeApiCallbackRemoveDto = gson.fromJson(response,
|
||||
BridgeApiCallbackRemoveDto.class);
|
||||
logger.debug("getBridgeCallbackRemove OK");
|
||||
return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(),
|
||||
bridgeApiCallbackRemoveDto);
|
||||
} else {
|
||||
logger.debug("Could not execute Callback Remove! Status[{}] - Response[{}]", status, response);
|
||||
return new BridgeCallbackRemoveResponse(status, contentResponse.getReason(), null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.debug("Could not execute Callback Remove! Exception[{}]", e.getMessage());
|
||||
return new BridgeCallbackRemoveResponse(handleException(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiCallbackAddDto} class defines the Data Transfer Object (POJO)
|
||||
* for the Nuki Bridge API /callback/add endpoint.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeApiCallbackAddDto {
|
||||
|
||||
private boolean success;
|
||||
private String message;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiCallbackListCallbackDto} class defines the Data Transfer Object (POJO)
|
||||
* for the Nuki Bridge API /callback/list endpoint.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeApiCallbackListCallbackDto {
|
||||
|
||||
private int id;
|
||||
private String url;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiCallbackListDto} class defines the Data Transfer Object (POJO)
|
||||
* for the Nuki Bridge API /callback/list endpoint.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeApiCallbackListDto {
|
||||
|
||||
private List<BridgeApiCallbackListCallbackDto> callbacks;
|
||||
|
||||
public List<BridgeApiCallbackListCallbackDto> getCallbacks() {
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
public void setCallbacks(List<BridgeApiCallbackListCallbackDto> callbacks) {
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiCallbackRemoveDto} class defines the Data Transfer Object (POJO)
|
||||
* for the Nuki Bridge API /callback/remove endpoint.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeApiCallbackRemoveDto {
|
||||
|
||||
private boolean success;
|
||||
private String message;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiInfoDto} class defines the Data Transfer Object (POJO) for the Nuki Bridge API /info endpoint.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeApiInfoDto {
|
||||
|
||||
private int bridgeType;
|
||||
private BridgeApiInfoIdDto ids;
|
||||
private BridgeApiInfoVersionDto versions;
|
||||
private int uptime;
|
||||
private String currentTime;
|
||||
private boolean serverConnected;
|
||||
private List<BridgeApiInfoScanResultDto> scanResults;
|
||||
|
||||
public int getBridgeType() {
|
||||
return bridgeType;
|
||||
}
|
||||
|
||||
public void setBridgeType(int bridgeType) {
|
||||
this.bridgeType = bridgeType;
|
||||
}
|
||||
|
||||
public BridgeApiInfoIdDto getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public void setIds(BridgeApiInfoIdDto ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
public BridgeApiInfoVersionDto getVersions() {
|
||||
return versions;
|
||||
}
|
||||
|
||||
public void setVersions(BridgeApiInfoVersionDto versions) {
|
||||
this.versions = versions;
|
||||
}
|
||||
|
||||
public int getUptime() {
|
||||
return uptime;
|
||||
}
|
||||
|
||||
public void setUptime(int uptime) {
|
||||
this.uptime = uptime;
|
||||
}
|
||||
|
||||
public String getCurrentTime() {
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
public void setCurrentTime(String currentTime) {
|
||||
this.currentTime = currentTime;
|
||||
}
|
||||
|
||||
public boolean isServerConnected() {
|
||||
return serverConnected;
|
||||
}
|
||||
|
||||
public void setServerConnected(boolean serverConnected) {
|
||||
this.serverConnected = serverConnected;
|
||||
}
|
||||
|
||||
public List<BridgeApiInfoScanResultDto> getScanResults() {
|
||||
return scanResults;
|
||||
}
|
||||
|
||||
public void setScanResults(List<BridgeApiInfoScanResultDto> scanResults) {
|
||||
this.scanResults = scanResults;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiInfoIdDto} class defines the Data Transfer Object (POJO) for the Nuki Bridge API /info endpoint.
|
||||
* It is a nested JSON object of {@link BridgeApiInfoDto}.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeApiInfoIdDto {
|
||||
|
||||
private int hardwareId;
|
||||
private int serverId;
|
||||
|
||||
public int getHardwareId() {
|
||||
return hardwareId;
|
||||
}
|
||||
|
||||
public void setHardwareId(int hardwareId) {
|
||||
this.hardwareId = hardwareId;
|
||||
}
|
||||
|
||||
public int getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public void setServerId(int serverId) {
|
||||
this.serverId = serverId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiInfoScanResultDto} class defines the Data Transfer Object (POJO) for the Nuki Bridge API /info
|
||||
* endpoint.
|
||||
* It is a nested JSON object of {@link BridgeApiInfoDto}.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeApiInfoScanResultDto {
|
||||
|
||||
private int nukiId;
|
||||
private String name;
|
||||
private int rssi;
|
||||
private boolean paired;
|
||||
|
||||
public int getNukiId() {
|
||||
return nukiId;
|
||||
}
|
||||
|
||||
public void setNukiId(int nukiId) {
|
||||
this.nukiId = nukiId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getRssi() {
|
||||
return rssi;
|
||||
}
|
||||
|
||||
public void setRssi(int rssi) {
|
||||
this.rssi = rssi;
|
||||
}
|
||||
|
||||
public boolean isPaired() {
|
||||
return paired;
|
||||
}
|
||||
|
||||
public void setPaired(boolean paired) {
|
||||
this.paired = paired;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiInfoVersionDto} class defines the Data Transfer Object (POJO) for the Nuki Bridge API /list
|
||||
* endpoint.
|
||||
* It is a nested JSON object of {@link BridgeApiInfoDto}.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeApiInfoVersionDto {
|
||||
|
||||
private String firmwareVersion;
|
||||
private String wifiFirmwareVersion;
|
||||
|
||||
public String getFirmwareVersion() {
|
||||
return firmwareVersion;
|
||||
}
|
||||
|
||||
public void setFirmwareVersion(String firmwareVersion) {
|
||||
this.firmwareVersion = firmwareVersion;
|
||||
}
|
||||
|
||||
public String getWifiFirmwareVersion() {
|
||||
return wifiFirmwareVersion;
|
||||
}
|
||||
|
||||
public void setWifiFirmwareVersion(String wifiFirmwareVersion) {
|
||||
this.wifiFirmwareVersion = wifiFirmwareVersion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiLockActionDto} class defines the Data Transfer Object (POJO) for the Nuki Bridge API /lockAction
|
||||
* endpoint.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class BridgeApiLockActionDto {
|
||||
|
||||
private boolean success;
|
||||
private boolean batteryCritical;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public boolean isBatteryCritical() {
|
||||
return batteryCritical;
|
||||
}
|
||||
|
||||
public void setBatteryCritical(boolean batteryCritical) {
|
||||
this.batteryCritical = batteryCritical;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiLockStateDto} class defines the Data Transfer Object (POJO) for the Nuki Bridge API /lockState
|
||||
* endpoint.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
* @contributer Christian Hoefler - Door sensor integration
|
||||
*/
|
||||
public class BridgeApiLockStateDto {
|
||||
|
||||
private int state;
|
||||
private String stateName;
|
||||
private boolean batteryCritical;
|
||||
private int doorsensorState;
|
||||
private boolean success;
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getStateName() {
|
||||
return stateName;
|
||||
}
|
||||
|
||||
public void setStateName(String stateName) {
|
||||
this.stateName = stateName;
|
||||
}
|
||||
|
||||
public boolean isBatteryCritical() {
|
||||
return batteryCritical;
|
||||
}
|
||||
|
||||
public void setBatteryCritical(boolean batteryCritical) {
|
||||
this.batteryCritical = batteryCritical;
|
||||
}
|
||||
|
||||
public int getDoorsensorState() {
|
||||
return doorsensorState;
|
||||
}
|
||||
|
||||
public void setDoorsensorState(int doorsensorState) {
|
||||
this.doorsensorState = doorsensorState;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link BridgeApiLockStateRequestDto} class defines the Data Transfer Object (POJO) which is send from the Nuki
|
||||
* Bridge to the openHAB Server.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
* @contributer Christian Hoefler - Door sensor integration
|
||||
*/
|
||||
public class BridgeApiLockStateRequestDto {
|
||||
|
||||
private int nukiId;
|
||||
private int state;
|
||||
private String stateName;
|
||||
private boolean batteryCritical;
|
||||
private int doorsensorState;
|
||||
|
||||
public int getNukiId() {
|
||||
return nukiId;
|
||||
}
|
||||
|
||||
public void setNukiId(int nukiId) {
|
||||
this.nukiId = nukiId;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getStateName() {
|
||||
return stateName;
|
||||
}
|
||||
|
||||
public void setStateName(String stateName) {
|
||||
this.stateName = stateName;
|
||||
}
|
||||
|
||||
public boolean isBatteryCritical() {
|
||||
return batteryCritical;
|
||||
}
|
||||
|
||||
public void setBatteryCritical(boolean batteryCritical) {
|
||||
this.batteryCritical = batteryCritical;
|
||||
}
|
||||
|
||||
public int getDoorsensorState() {
|
||||
return doorsensorState;
|
||||
}
|
||||
|
||||
public void setDoorsensorState(int doorsensorState) {
|
||||
this.doorsensorState = doorsensorState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.dto;
|
||||
|
||||
/**
|
||||
* The {@link NukiHttpServerStatusResponseDto} class defines the Data Transfer Object (POJO) for a status response of
|
||||
* the Nuki HTTP
|
||||
* Server.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class NukiHttpServerStatusResponseDto {
|
||||
|
||||
private String status;
|
||||
|
||||
public NukiHttpServerStatusResponseDto(String status) {
|
||||
super();
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.handler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.nuki.internal.NukiBindingConstants;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.BridgeCallbackAddResponse;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.BridgeCallbackListResponse;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.BridgeCallbackRemoveResponse;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.BridgeInfoResponse;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.NukiHttpClient;
|
||||
import org.openhab.binding.nuki.internal.dto.BridgeApiCallbackListCallbackDto;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link NukiBridgeHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
*/
|
||||
public class NukiBridgeHandler extends BaseBridgeHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(NukiBridgeHandler.class);
|
||||
private static final int JOB_INTERVAL = 600;
|
||||
|
||||
private HttpClient httpClient;
|
||||
private NukiHttpClient nukiHttpClient;
|
||||
private String callbackUrl;
|
||||
private ScheduledFuture<?> checkBridgeOnlineJob;
|
||||
private String bridgeIp;
|
||||
private boolean manageCallbacks;
|
||||
private boolean initializable;
|
||||
|
||||
public NukiBridgeHandler(Bridge bridge, HttpClient httpClient, String callbackUrl) {
|
||||
super(bridge);
|
||||
logger.debug("Instantiating NukiBridgeHandler({}, {}, {})", bridge, httpClient, callbackUrl);
|
||||
this.httpClient = httpClient;
|
||||
this.callbackUrl = callbackUrl;
|
||||
this.initializable = getConfig().get(NukiBindingConstants.CONFIG_IP) != null
|
||||
&& getConfig().get(NukiBindingConstants.CONFIG_API_TOKEN) != null;
|
||||
}
|
||||
|
||||
public NukiHttpClient getNukiHttpClient() {
|
||||
if (nukiHttpClient == null) {
|
||||
nukiHttpClient = new NukiHttpClient(httpClient, getConfig());
|
||||
}
|
||||
return nukiHttpClient;
|
||||
}
|
||||
|
||||
public boolean isInitializable() {
|
||||
return initializable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("initialize() for Bridge[{}].", getThing().getUID());
|
||||
Configuration config = getConfig();
|
||||
bridgeIp = (String) config.get(NukiBindingConstants.CONFIG_IP);
|
||||
manageCallbacks = (Boolean) config.get(NukiBindingConstants.CONFIG_MANAGECB);
|
||||
if (bridgeIp == null) {
|
||||
logger.debug("NukiBridgeHandler[{}] is not initializable, IP setting is unset in the configuration!",
|
||||
getThing().getUID());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "IP setting is unset");
|
||||
} else if (config.get(NukiBindingConstants.CONFIG_API_TOKEN) == null) {
|
||||
logger.debug("NukiBridgeHandler[{}] is not initializable, apiToken setting is unset in the configuration!",
|
||||
getThing().getUID());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "apiToken setting is unset");
|
||||
} else {
|
||||
scheduler.execute(this::initializeHandler);
|
||||
checkBridgeOnlineJob = scheduler.scheduleWithFixedDelay(this::checkBridgeOnline, JOB_INTERVAL, JOB_INTERVAL,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommand({}, {}) for Bridge[{}] not implemented!", channelUID, command, bridgeIp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("dispose() for Bridge[{}].", getThing().getUID());
|
||||
nukiHttpClient = null;
|
||||
if (checkBridgeOnlineJob != null && !checkBridgeOnlineJob.isCancelled()) {
|
||||
checkBridgeOnlineJob.cancel(true);
|
||||
}
|
||||
checkBridgeOnlineJob = null;
|
||||
}
|
||||
|
||||
private synchronized void initializeHandler() {
|
||||
logger.debug("initializeHandler() for Bridge[{}].", bridgeIp);
|
||||
BridgeInfoResponse bridgeInfoResponse = getNukiHttpClient().getBridgeInfo();
|
||||
if (bridgeInfoResponse.getStatus() == HttpStatus.OK_200) {
|
||||
if (manageCallbacks) {
|
||||
manageNukiBridgeCallbacks();
|
||||
}
|
||||
logger.debug("Bridge[{}] responded with status[{}]. Switching the bridge online.", bridgeIp,
|
||||
bridgeInfoResponse.getStatus());
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
logger.debug("Bridge[{}] responded with status[{}]. Switching the bridge offline!", bridgeIp,
|
||||
bridgeInfoResponse.getStatus());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, bridgeInfoResponse.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBridgeOnline() {
|
||||
logger.debug("checkBridgeOnline():bridgeIp[{}] status[{}]", bridgeIp, getThing().getStatus());
|
||||
if (getThing().getStatus().equals(ThingStatus.ONLINE)) {
|
||||
logger.debug("Requesting BridgeInfo to ensure Bridge[{}] is online.", bridgeIp);
|
||||
BridgeInfoResponse bridgeInfoResponse = getNukiHttpClient().getBridgeInfo();
|
||||
int status = bridgeInfoResponse.getStatus();
|
||||
if (status == HttpStatus.OK_200) {
|
||||
logger.debug("Bridge[{}] responded with status[{}]. Bridge is online.", bridgeIp, status);
|
||||
} else if (status == HttpStatus.SERVICE_UNAVAILABLE_503) {
|
||||
logger.debug(
|
||||
"Bridge[{}] responded with status[{}]. REST service seems to be busy but Bridge is online.",
|
||||
bridgeIp, status);
|
||||
} else {
|
||||
logger.debug("Bridge[{}] responded with status[{}]. Switching the bridge offline!", bridgeIp, status);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
bridgeInfoResponse.getMessage());
|
||||
}
|
||||
} else {
|
||||
initializeHandler();
|
||||
}
|
||||
}
|
||||
|
||||
private void manageNukiBridgeCallbacks() {
|
||||
logger.debug("manageNukiBridgeCallbacks() for Bridge[{}].", bridgeIp);
|
||||
BridgeCallbackListResponse bridgeCallbackListResponse = getNukiHttpClient().getBridgeCallbackList();
|
||||
List<BridgeApiCallbackListCallbackDto> callbacks = bridgeCallbackListResponse.getCallbacks();
|
||||
boolean callbackExists = false;
|
||||
int callbackCount = callbacks == null ? 0 : callbacks.size();
|
||||
if (callbacks != null) {
|
||||
for (BridgeApiCallbackListCallbackDto callback : callbacks) {
|
||||
if (callback.getUrl().equals(callbackUrl)) {
|
||||
logger.debug("callbackUrl[{}] already existing on Bridge[{}].", callbackUrl, bridgeIp);
|
||||
callbackExists = true;
|
||||
continue;
|
||||
}
|
||||
if (callback.getUrl().contains(NukiBindingConstants.CALLBACK_ENDPOINT)) {
|
||||
logger.debug("Partial callbackUrl[{}] found on Bridge[{}] - Removing it!", callbackUrl, bridgeIp);
|
||||
BridgeCallbackRemoveResponse bridgeCallbackRemoveResponse = getNukiHttpClient()
|
||||
.getBridgeCallbackRemove(callback.getId());
|
||||
if (bridgeCallbackRemoveResponse.getStatus() == HttpStatus.OK_200) {
|
||||
logger.debug("Successfully removed callbackUrl[{}] on Bridge[{}]!", callbackUrl, bridgeIp);
|
||||
callbackCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!callbackExists) {
|
||||
if (callbackCount == 3) {
|
||||
logger.debug("Already 3 callback URLs existing on Bridge[{}] - Removing ID 0!", bridgeIp);
|
||||
BridgeCallbackRemoveResponse bridgeCallbackRemoveResponse = getNukiHttpClient()
|
||||
.getBridgeCallbackRemove(0);
|
||||
if (bridgeCallbackRemoveResponse.getStatus() == HttpStatus.OK_200) {
|
||||
logger.debug("Successfully removed callbackUrl[{}] on Bridge[{}]!", callbackUrl, bridgeIp);
|
||||
callbackCount--;
|
||||
}
|
||||
}
|
||||
logger.debug("Adding callbackUrl[{}] to Bridge[{}]!", callbackUrl, bridgeIp);
|
||||
BridgeCallbackAddResponse bridgeCallbackAddResponse = getNukiHttpClient().getBridgeCallbackAdd(callbackUrl);
|
||||
if (bridgeCallbackAddResponse.getStatus() == HttpStatus.OK_200) {
|
||||
logger.debug("Successfully added callbackUrl[{}] on Bridge[{}]!", callbackUrl, bridgeIp);
|
||||
callbackExists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.nuki.internal.handler;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openhab.binding.nuki.internal.NukiBindingConstants;
|
||||
import org.openhab.binding.nuki.internal.converter.LockActionConverter;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.BridgeLockActionResponse;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.BridgeLockStateResponse;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.NukiBaseResponse;
|
||||
import org.openhab.binding.nuki.internal.dataexchange.NukiHttpClient;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link NukiSmartLockHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Markus Katter - Initial contribution
|
||||
* @contributer Christian Hoefler - Door sensor integration
|
||||
*/
|
||||
public class NukiSmartLockHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(NukiSmartLockHandler.class);
|
||||
private static final int JOB_INTERVAL = 60;
|
||||
|
||||
private NukiHttpClient nukiHttpClient;
|
||||
private ScheduledFuture<?> reInitJob;
|
||||
private String nukiId;
|
||||
private boolean unlatch;
|
||||
|
||||
public NukiSmartLockHandler(Thing thing) {
|
||||
super(thing);
|
||||
logger.debug("Instantiating NukiSmartLockHandler({})", thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("initialize() for Smart Lock[{}].", getThing().getUID());
|
||||
Configuration config = getConfig();
|
||||
nukiId = (String) config.get(NukiBindingConstants.CONFIG_NUKI_ID);
|
||||
unlatch = (Boolean) config.get(NukiBindingConstants.CONFIG_UNLATCH);
|
||||
if (nukiId == null) {
|
||||
logger.debug("NukiSmartLockHandler[{}] is not initializable, nukiId setting is unset in the configuration!",
|
||||
getThing().getUID());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "nukiId setting is unset");
|
||||
} else {
|
||||
scheduler.execute(this::initializeHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("dispose() for Smart Lock[{}].", getThing().getUID());
|
||||
stopReInitJob();
|
||||
}
|
||||
|
||||
private void initializeHandler() {
|
||||
logger.debug("initializeHandler() for Smart Lock[{}]", nukiId);
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
initializeHandler(null, null);
|
||||
} else {
|
||||
initializeHandler(bridge.getHandler(), bridge.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeHandler(ThingHandler bridgeHandler, ThingStatus bridgeStatus) {
|
||||
if (bridgeHandler != null && bridgeStatus != null) {
|
||||
if (bridgeStatus == ThingStatus.ONLINE) {
|
||||
nukiHttpClient = ((NukiBridgeHandler) bridgeHandler).getNukiHttpClient();
|
||||
BridgeLockStateResponse bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
|
||||
if (handleResponse(bridgeLockStateResponse, null, null)) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
for (Channel channel : thing.getChannels()) {
|
||||
handleCommand(channel.getUID(), RefreshType.REFRESH);
|
||||
}
|
||||
stopReInitJob();
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
stopReInitJob();
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED);
|
||||
stopReInitJob();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
logger.debug("bridgeStatusChanged({}) for Smart Lock[{}].", bridgeStatusInfo, nukiId);
|
||||
scheduler.execute(() -> {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
initializeHandler(null, bridgeStatusInfo.getStatus());
|
||||
} else {
|
||||
initializeHandler(bridge.getHandler(), bridgeStatusInfo.getStatus());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommand({}, {})", channelUID, command);
|
||||
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
logger.debug("Thing is not ONLINE; command[{}] for channelUID[{}] is ignored", command, channelUID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command instanceof RefreshType) {
|
||||
handleCommandRefreshType(channelUID, command);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean validCmd = true;
|
||||
switch (channelUID.getId()) {
|
||||
case NukiBindingConstants.CHANNEL_SMARTLOCK_LOCK:
|
||||
if (command instanceof OnOffType) {
|
||||
int lockAction;
|
||||
if (unlatch) {
|
||||
lockAction = (command == OnOffType.OFF ? NukiBindingConstants.LOCK_ACTIONS_UNLATCH
|
||||
: NukiBindingConstants.LOCK_ACTIONS_LOCK);
|
||||
} else {
|
||||
lockAction = (command == OnOffType.OFF ? NukiBindingConstants.LOCK_ACTIONS_UNLOCK
|
||||
: NukiBindingConstants.LOCK_ACTIONS_LOCK);
|
||||
}
|
||||
Channel channelLockState = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_STATE);
|
||||
if (channelLockState != null) {
|
||||
updateState(channelLockState.getUID(),
|
||||
new DecimalType(LockActionConverter.getLockStateFor(lockAction)));
|
||||
}
|
||||
BridgeLockActionResponse bridgeLockActionResponse = nukiHttpClient.getBridgeLockAction(nukiId,
|
||||
lockAction);
|
||||
handleResponse(bridgeLockActionResponse, channelUID.getAsString(), command.toString());
|
||||
} else {
|
||||
validCmd = false;
|
||||
}
|
||||
break;
|
||||
case NukiBindingConstants.CHANNEL_SMARTLOCK_STATE:
|
||||
if (command instanceof DecimalType) {
|
||||
int lockAction;
|
||||
lockAction = ((DecimalType) command).intValue();
|
||||
lockAction = LockActionConverter.getLockActionFor(lockAction);
|
||||
updateState(channelUID, new DecimalType(LockActionConverter.getLockStateFor(lockAction)));
|
||||
BridgeLockActionResponse bridgeLockActionResponse = nukiHttpClient.getBridgeLockAction(nukiId,
|
||||
lockAction);
|
||||
handleResponse(bridgeLockActionResponse, channelUID.getAsString(), command.toString());
|
||||
} else {
|
||||
validCmd = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
validCmd = false;
|
||||
break;
|
||||
}
|
||||
if (!validCmd) {
|
||||
logger.debug("Unexpected command[{}] for channelUID[{}]!", command, channelUID);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCommandRefreshType(ChannelUID channelUID, Command command) {
|
||||
logger.debug("handleCommandRefreshType({}, {})", channelUID, command);
|
||||
BridgeLockStateResponse bridgeLockStateResponse;
|
||||
switch (channelUID.getId()) {
|
||||
case NukiBindingConstants.CHANNEL_SMARTLOCK_LOCK:
|
||||
bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
|
||||
if (handleResponse(bridgeLockStateResponse, channelUID.getAsString(), command.toString())) {
|
||||
int lockState = bridgeLockStateResponse.getState();
|
||||
State state;
|
||||
if (lockState == NukiBindingConstants.LOCK_STATES_LOCKED) {
|
||||
state = OnOffType.ON;
|
||||
} else if (lockState == NukiBindingConstants.LOCK_STATES_UNLOCKED) {
|
||||
state = OnOffType.OFF;
|
||||
} else {
|
||||
logger.warn(
|
||||
"Smart Lock returned lockState[{}]. Intentionally setting possibly wrong value 'OFF' for channel 'smartlockLock'!",
|
||||
lockState);
|
||||
state = OnOffType.OFF;
|
||||
}
|
||||
updateState(channelUID, state);
|
||||
}
|
||||
break;
|
||||
case NukiBindingConstants.CHANNEL_SMARTLOCK_STATE:
|
||||
bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
|
||||
if (handleResponse(bridgeLockStateResponse, channelUID.getAsString(), command.toString())) {
|
||||
updateState(channelUID, new DecimalType(bridgeLockStateResponse.getState()));
|
||||
}
|
||||
break;
|
||||
case NukiBindingConstants.CHANNEL_SMARTLOCK_LOW_BATTERY:
|
||||
bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
|
||||
if (handleResponse(bridgeLockStateResponse, channelUID.getAsString(), command.toString())) {
|
||||
updateState(channelUID, bridgeLockStateResponse.isBatteryCritical() ? OnOffType.ON : OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case NukiBindingConstants.CHANNEL_SMARTLOCK_DOOR_STATE:
|
||||
bridgeLockStateResponse = nukiHttpClient.getBridgeLockState(nukiId);
|
||||
if (handleResponse(bridgeLockStateResponse, channelUID.getAsString(), command.toString())) {
|
||||
updateState(channelUID, new DecimalType(bridgeLockStateResponse.getDoorsensorState()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Command[{}] for channelUID[{}] not implemented!", command, channelUID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleResponse(NukiBaseResponse nukiBaseResponse, String channelUID, String command) {
|
||||
if (nukiBaseResponse.getStatus() == 200 && nukiBaseResponse.isSuccess()) {
|
||||
logger.debug("Command[{}] succeeded for channelUID[{}] on nukiId[{}]!", command, channelUID, nukiId);
|
||||
return true;
|
||||
} else if (nukiBaseResponse.getStatus() != 200) {
|
||||
logger.debug("Request to Bridge failed! status[{}] - message[{}]", nukiBaseResponse.getStatus(),
|
||||
nukiBaseResponse.getMessage());
|
||||
} else if (!nukiBaseResponse.isSuccess()) {
|
||||
logger.debug(
|
||||
"Request from Bridge to Smart Lock failed! status[{}] - message[{}] - isSuccess[{}]. Check if Nuki Smart Lock is powered on!",
|
||||
nukiBaseResponse.getStatus(), nukiBaseResponse.getMessage(), nukiBaseResponse.isSuccess());
|
||||
}
|
||||
logger.debug("Could not handle command[{}] for channelUID[{}] on nukiId[{}]!", command, channelUID, nukiId);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, nukiBaseResponse.getMessage());
|
||||
Channel channelLock = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_LOCK);
|
||||
if (channelLock != null) {
|
||||
updateState(channelLock.getUID(), OnOffType.OFF);
|
||||
}
|
||||
Channel channelLockState = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_STATE);
|
||||
if (channelLockState != null) {
|
||||
updateState(channelLockState.getUID(), new DecimalType(NukiBindingConstants.LOCK_STATES_UNDEFINED));
|
||||
}
|
||||
Channel channelDoorState = thing.getChannel(NukiBindingConstants.CHANNEL_SMARTLOCK_DOOR_STATE);
|
||||
if (channelDoorState != null) {
|
||||
updateState(channelDoorState.getUID(), new DecimalType(NukiBindingConstants.DOORSENSOR_STATES_UNKNOWN));
|
||||
}
|
||||
startReInitJob();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void startReInitJob() {
|
||||
logger.trace("Starting reInitJob with interval of {}secs for Smart Lock[{}].", JOB_INTERVAL, nukiId);
|
||||
if (reInitJob != null) {
|
||||
logger.trace("Already started reInitJob for Smart Lock[{}].", nukiId);
|
||||
return;
|
||||
}
|
||||
reInitJob = scheduler.scheduleWithFixedDelay(this::initializeHandler, JOB_INTERVAL, JOB_INTERVAL,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void stopReInitJob() {
|
||||
logger.trace("Stopping reInitJob for Smart Lock[{}].", nukiId);
|
||||
if (reInitJob != null && !reInitJob.isCancelled()) {
|
||||
logger.trace("Stopped reInitJob for Smart Lock[{}].", nukiId);
|
||||
reInitJob.cancel(true);
|
||||
}
|
||||
reInitJob = null;
|
||||
}
|
||||
|
||||
public void handleApiServletUpdate(ChannelUID channelUID, State newState) {
|
||||
logger.trace("handleApiServletUpdate({}, {})", channelUID, newState);
|
||||
updateState(channelUID, newState);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="nuki" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>Nuki Binding</name>
|
||||
<description>The Nuki Binding allows simple and fast integration of Nuki Smart Locks into openHAB. This binding needs
|
||||
the Nuki Smart Lock(s) to be paired via Bluetooth with a Nuki Bridge to function correctly.</description>
|
||||
<author>Markus Katter</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="nuki"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Nuki Bridge (Bridge Type) -->
|
||||
<bridge-type id="bridge">
|
||||
<label>Nuki Bridge</label>
|
||||
<description>This bridge represents a Nuki Bridge on your local network. Nuki Smart Locks have to be paired via
|
||||
Bluetooth with it.</description>
|
||||
<config-description>
|
||||
<parameter name="ip" type="text" required="true">
|
||||
<label>IP Address</label>
|
||||
<context>network-address</context>
|
||||
<description>The IP address of the Nuki Bridge. Look it up on your router. It is recommended to set a static IP
|
||||
address lease for the Nuki Bridge (and for your openHAB server too) on your router.</description>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" required="false">
|
||||
<label>Port</label>
|
||||
<description>The Port which you configured during Initial Bridge setup
|
||||
(https://nuki.io/en/support/bridge/bridge-setup/initial-bridge-setup/).</description>
|
||||
<default>8080</default>
|
||||
</parameter>
|
||||
<parameter name="apiToken" type="text" required="true">
|
||||
<label>API Token</label>
|
||||
<context>password</context>
|
||||
<description>The API Token which you configured during Initial Bridge setup
|
||||
(https://nuki.io/en/support/bridge/bridge-setup/initial-bridge-setup/).</description>
|
||||
</parameter>
|
||||
<parameter name="manageCallbacks" type="boolean" required="false">
|
||||
<label>Manage Nuki Bridge Callbacks</label>
|
||||
<description>Let the Nuki Binding manage the callback on the Nuki Bridge.</description>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<!-- Nuki Smart Lock (Thing Type) -->
|
||||
<thing-type id="smartlock">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Nuki Smart Lock</label>
|
||||
<description>Nuki Smart Lock which is paired via Bluetooth to a Nuki Bridge.</description>
|
||||
<channels>
|
||||
<channel id="lock" typeId="smartlockLock"/>
|
||||
<channel id="lockState" typeId="smartlockState"/>
|
||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||
<channel id="doorsensorState" typeId="smartlockDoorState"/>
|
||||
</channels>
|
||||
<config-description>
|
||||
<parameter name="nukiId" type="text" required="true">
|
||||
<label>Nuki ID</label>
|
||||
<description>The 8-digit hexadecimal string that identifies the Nuki Smart Lock. Look it up on the sticker on the
|
||||
back of the Nuki Smart Lock (remove mounting plate).</description>
|
||||
</parameter>
|
||||
<parameter name="unlatch" type="boolean" required="false">
|
||||
<label>Unlatch</label>
|
||||
<description>If switched to On (or set to true) the Nuki Smart Lock will unlock the door but then also automatically
|
||||
pull the latch of the door lock. Usually, if the door hinges are correctly adjusted, the door will then swing open.</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<!-- Channel Type -->
|
||||
<channel-type id="smartlockLock">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Lock</label>
|
||||
<description>Use this channel with a Switch Item to unlock and lock the door. Configure "Unlatch" to true if your Nuki
|
||||
Smart Lock is mounted on a door lock with a knob on the outside.</description>
|
||||
<category>Door</category>
|
||||
<state>
|
||||
<options>
|
||||
<option value="OFF">Unlocks the door</option>
|
||||
<option value="ON">Locks the door</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="smartlockState">
|
||||
<item-type>Number</item-type>
|
||||
<label>Lock State</label>
|
||||
<description>Use this channel if you want to execute other supported lock actions or to display the current lock
|
||||
state.</description>
|
||||
<category>Door</category>
|
||||
<state>
|
||||
<options>
|
||||
<option value="0">Uncalibrated</option>
|
||||
<option value="1">Locked</option>
|
||||
<option value="2">Unlocking</option>
|
||||
<option value="3">Unlocked</option>
|
||||
<option value="4">Locking</option>
|
||||
<option value="5">Unlatched</option>
|
||||
<option value="6">Unlocked (Lock 'n' Go)</option>
|
||||
<option value="7">Unlatching</option>
|
||||
<option value="1002">Unlocking (Lock 'n' Go)</option>
|
||||
<option value="1007">Unlatching (Lock 'n' Go)</option>
|
||||
<option value="254">Motor blocked</option>
|
||||
<option value="255">UNDEFINED</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="smartlockDoorState">
|
||||
<item-type>Number</item-type>
|
||||
<label>Door State</label>
|
||||
<description>Use this channel to display the current state of the door sensor</description>
|
||||
<category>Door</category>
|
||||
<state>
|
||||
<options>
|
||||
<option value="0">Unavailable</option>
|
||||
<option value="1">Deactivated</option>
|
||||
<option value="2">Closed</option>
|
||||
<option value="3">Open</option>
|
||||
<option value="4">Unknown</option>
|
||||
<option value="5">Calibrating</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user