added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.vektiva-${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-vektiva" description="Vektiva 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.vektiva/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,46 @@
/**
* 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.vektiva.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link VektivaBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Ondrej Pecta - Initial contribution
*/
@NonNullByDefault
public class VektivaBindingConstants {
private static final String BINDING_ID = "vektiva";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_SMARWI = new ThingTypeUID(BINDING_ID, "smarwi");
// List of all Channel ids
public static final String CHANNEL_CONTROL = "control";
public static final String CHANNEL_STATUS = "status";
// commands
public static final String COMMAND_OPEN = "open";
public static final String COMMAND_CLOSE = "close";
public static final String COMMAND_STOP = "stop";
// response
public static final String RESPONSE_OK = "OK";
// constants
public static final String NA = "N/A";
}

View File

@@ -0,0 +1,80 @@
/**
* 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.vektiva.internal;
import static org.openhab.binding.vektiva.internal.VektivaBindingConstants.THING_TYPE_SMARWI;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.binding.vektiva.internal.handler.VektivaSmarwiHandler;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.io.net.http.WebSocketFactory;
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;
/**
* The {@link VektivaHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Ondrej Pecta - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.vektiva", service = ThingHandlerFactory.class)
public class VektivaHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_SMARWI);
/**
* the shared http client
*/
private @NonNullByDefault({}) HttpClient httpClient;
/**
* the shared web socket client
*/
private @NonNullByDefault({}) WebSocketClient webSocketClient;
@Activate
public VektivaHandlerFactory(@Reference HttpClientFactory httpClientFactory,
@Reference WebSocketFactory webSocketFactory) {
this.httpClient = httpClientFactory.getCommonHttpClient();
this.webSocketClient = webSocketFactory.getCommonWebSocketClient();
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_SMARWI.equals(thingTypeUID)) {
return new VektivaSmarwiHandler(thing, httpClient, webSocketClient);
}
return null;
}
}

View File

@@ -0,0 +1,31 @@
/**
* 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.vektiva.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link VektivaSmarwiConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Ondrej Pecta - Initial contribution
*/
@NonNullByDefault
public class VektivaSmarwiConfiguration {
/**
* Smarwi configuration parameter.
*/
public String ip = "";
public int refreshInterval = 30;
public boolean useWebSockets = false;
}

View File

@@ -0,0 +1,272 @@
/**
* 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.vektiva.internal.handler;
import static org.openhab.binding.vektiva.internal.VektivaBindingConstants.*;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.binding.vektiva.internal.config.VektivaSmarwiConfiguration;
import org.openhab.binding.vektiva.internal.net.VektivaSmarwiSocket;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StopMoveType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.UpDownType;
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.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link VektivaSmarwiHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Ondrej Pecta - Initial contribution
*/
@NonNullByDefault
public class VektivaSmarwiHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(VektivaSmarwiHandler.class);
private VektivaSmarwiConfiguration config = new VektivaSmarwiConfiguration();
private final HttpClient httpClient;
private final WebSocketClient webSocketClient;
private @Nullable Session session;
private @Nullable ScheduledFuture<?> future = null;
private int lastPosition = -1;
public VektivaSmarwiHandler(Thing thing, HttpClient httpClient, WebSocketClient webSocketClient) {
super(thing);
this.httpClient = httpClient;
this.webSocketClient = webSocketClient;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (CHANNEL_STATUS.equals(channelUID.getId()) && command instanceof RefreshType) {
checkStatus();
return;
}
if (CHANNEL_CONTROL.equals(channelUID.getId())) {
logger.trace("Received command: {}", command);
String cmd = getSmarwiCommand(command);
if (COMMAND_OPEN.equals(cmd) || COMMAND_CLOSE.equals(cmd) || COMMAND_STOP.equals(cmd)) {
if (RESPONSE_OK.equals(sendCommand(cmd)) && !COMMAND_STOP.equals(cmd)) {
lastPosition = COMMAND_OPEN.equals(cmd) ? 0 : 100;
} else {
lastPosition = -1;
}
}
if (command instanceof PercentType) {
if (RESPONSE_OK.equals(sendCommand(COMMAND_OPEN + "/" + cmd))) {
lastPosition = Integer.parseInt(cmd);
}
}
}
}
@Override
public void dispose() {
super.dispose();
if (future != null && !(future.isCancelled() || future.isDone())) {
future.cancel(true);
}
closeSession();
}
private void closeSession() {
if (session != null && session.isOpen()) {
session.close();
}
}
private String getSmarwiCommand(Command command) {
if (UpDownType.UP.equals(command)) {
return COMMAND_OPEN;
}
if (UpDownType.DOWN.equals(command)) {
return COMMAND_CLOSE;
}
if (StopMoveType.STOP.equals(command)) {
return COMMAND_STOP;
}
return command.toString();
}
private @Nullable String sendCommand(String cmd) {
String url = "http://" + config.ip + "/cmd/" + cmd;
try {
ContentResponse resp = httpClient.newRequest(url).method(HttpMethod.GET).send();
final String response = resp.getContentAsString();
logger.trace("Response: {}", response);
if (resp.getStatus() == 200) {
if (ThingStatus.ONLINE != getThing().getStatus()) {
updateStatus(ThingStatus.ONLINE);
}
} else {
updateStatus(ThingStatus.OFFLINE);
}
return response;
} catch (InterruptedException e) {
logger.debug("API execution has been interrupted", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"API execution has been interrupted");
Thread.currentThread().interrupt();
} catch (TimeoutException e) {
logger.debug("Timeout during API execution", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Timeout during API execution");
} catch (ExecutionException e) {
logger.debug("Exception during API execution", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Exception during API execution: " + e.getMessage());
}
return null;
}
@Override
public void initialize() {
config = getConfigAs(VektivaSmarwiConfiguration.class);
logger.debug("IP address: {}", config.ip);
future = scheduler.scheduleWithFixedDelay(this::checkStatus, 0, config.refreshInterval, TimeUnit.SECONDS);
}
private synchronized void initializeWebSocketSession() {
if (config.useWebSockets) {
closeSession();
session = createSession();
if (session != null) {
logger.debug("WebSocket connected!");
}
}
}
private void checkStatus() {
String url = "http://" + config.ip + "/statusn";
try {
ContentResponse resp = httpClient.newRequest(url).method(HttpMethod.GET).send();
final String response = resp.getContentAsString();
logger.debug("status values: {}", response);
if (resp.getStatus() == 200) {
processStatusResponse(response);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"got response code: " + resp.getStatus());
}
// reconnect web socket if not connected
if (config.useWebSockets && (session == null || !session.isOpen())
&& ThingStatus.ONLINE == getThing().getStatus()) {
logger.debug("Initializing WebSocket session");
initializeWebSocketSession();
return;
}
} catch (InterruptedException e) {
logger.debug("API execution has been interrupted", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"API execution has been interrupted");
Thread.currentThread().interrupt();
} catch (TimeoutException e) {
logger.debug("Timeout during status update", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Timeout during status update");
} catch (ExecutionException e) {
logger.debug("Exception during status update", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Exception during status update: " + e.getMessage());
}
session = null;
}
public synchronized void processStatusResponse(String content) {
if (ThingStatus.ONLINE != getThing().getStatus()) {
updateStatus(ThingStatus.ONLINE);
}
Map<String, String> values = Stream.of(content.split("\n")).map(s -> s.split(":")).filter(s -> s.length == 2)
.collect(Collectors.toMap(s -> s[0], s -> s[1]));
updateProperty("Product type", values.getOrDefault("t", NA));
updateProperty(Thing.PROPERTY_FIRMWARE_VERSION, values.getOrDefault("fw", NA));
updateProperty("Wifi signal", values.getOrDefault("rssi", NA));
updateProperty("Product name", values.getOrDefault("cid", NA));
String statusMessage = "Stopped";
if (!"250".equals(values.getOrDefault("s", NA))) {
statusMessage = "Moving";
}
if ("1".equals(values.getOrDefault("ro", NA))) {
statusMessage = "Not ready";
}
if ("10".equals(values.getOrDefault("e", NA))) {
statusMessage = "Blocked";
}
int position = values.getOrDefault("pos", NA).equals("o") ? 0 : 100;
if (position == 0 && lastPosition != -1) {
position = lastPosition;
}
updateState(CHANNEL_CONTROL, new PercentType(position));
updateState(CHANNEL_STATUS, new StringType(statusMessage));
}
private @Nullable Session createSession() {
String url = "ws://" + config.ip + "/ws";
URI uri = URI.create(url);
try {
// The socket that receives events
VektivaSmarwiSocket socket = new VektivaSmarwiSocket(this);
// Attempt Connect
Future<Session> fut = webSocketClient.connect(socket, uri);
// Wait for Connect
return fut.get();
} catch (IOException ex) {
logger.debug("Cannot connect websocket client", ex);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot connect websocket client");
} catch (InterruptedException ex) {
logger.debug("Cannot create websocket session", ex);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot create websocket session");
Thread.currentThread().interrupt();
} catch (ExecutionException ex) {
logger.debug("Cannot create websocket session", ex);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot create websocket session");
}
return null;
}
}

View File

@@ -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.vektiva.internal.net;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.openhab.binding.vektiva.internal.handler.VektivaSmarwiHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link VektivaSmarwiSocket} class defines websocket used for connection with
* the Smarwi thing.
*
* @author Ondrej Pecta - Initial contribution
*/
@NonNullByDefault
public class VektivaSmarwiSocket extends WebSocketAdapter {
private final Logger logger = LoggerFactory.getLogger(VektivaSmarwiSocket.class);
private final VektivaSmarwiHandler handler;
public VektivaSmarwiSocket(VektivaSmarwiHandler handler) {
this.handler = handler;
}
@Override
public void onWebSocketText(@Nullable String message) {
super.onWebSocketText(message);
if (message != null) {
logger.trace("Got message: {}", message);
handler.processStatusResponse(message);
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="vektiva" 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>Vektiva Binding</name>
<description>This is the binding for Vektiva products controlled by local API.</description>
<author>Ondrej Pecta</author>
</binding:binding>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="vektiva"
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">
<!-- Smarwi Thing -->
<thing-type id="smarwi">
<label>Vektiva Smarwi Thing</label>
<description>Smarwi thing for Vektiva Binding</description>
<channels>
<channel id="control" typeId="control"/>
<channel id="status" typeId="status"/>
</channels>
<config-description>
<parameter name="ip" type="text" required="true">
<label>Smarwi IP Address</label>
<context>network-address</context>
<description>IP address of the Smarwi thing on the local network.</description>
</parameter>
<parameter name="refreshInterval" type="integer" required="true" min="5">
<label>Refresh Interval</label>
<description>The refresh interval to poll Smarwi thing (in s).</description>
<default>30</default>
<advanced>true</advanced>
</parameter>
<parameter name="useWebSockets" type="boolean">
<label>Use WebSockets Feature for Faster State Update.</label>
<description>Enable web sockets technology. FW 203.2.4+ required</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<!-- Control Channel Type -->
<channel-type id="control">
<item-type>Rollershutter</item-type>
<label>Control</label>
<description>Control channel for Smarwi thing.</description>
</channel-type>
<!-- Status Channel Type -->
<channel-type id="status">
<item-type>String</item-type>
<label>Status</label>
<description>Status of the Smarwi device (Stopped, Moving, Not ready, Blocked).</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>