added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.io.openhabcloud-${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-misc-openhabcloud" description="openHAB Cloud Connector" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<configfile finalname="${openhab.conf}/services/openhabcloud.cfg" override="false">mvn:${project.groupId}/openhab-addons-external/${project.version}/cfg/openhabcloud</configfile>
|
||||
<bundle dependency="true">mvn:org.json/json/20180813</bundle>
|
||||
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jsr305/3.0.2_1</bundle>
|
||||
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/3.8.1_1</bundle>
|
||||
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okio/1.13.0_1</bundle>
|
||||
<bundle dependency="true">mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.0</bundle>
|
||||
<bundle dependency="true">mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.0</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.io.openhabcloud/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 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.io.openhabcloud;
|
||||
|
||||
import org.openhab.core.model.script.engine.action.ActionDoc;
|
||||
import org.openhab.io.openhabcloud.internal.CloudService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class provides static methods that can be used in automation rules
|
||||
* for sending notifications to the native apps.
|
||||
*
|
||||
* @author Victor Belov - Initial contribution
|
||||
* @author Kai Kreuzer - migrated code to ESH APIs
|
||||
*
|
||||
*/
|
||||
|
||||
public class NotificationAction {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(NotificationAction.class);
|
||||
|
||||
public static CloudService cloudService = null;
|
||||
|
||||
/**
|
||||
* Sends a simple push notification to mobile devices of user
|
||||
*
|
||||
* @param userId the cloud user id of the recipient
|
||||
* @param message the body of the notification
|
||||
*
|
||||
*/
|
||||
@ActionDoc(text = "Sends a push notification to mobile devices of user with userId")
|
||||
public static void sendNotification(String userId, String message) {
|
||||
sendNotification(userId, message, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an advanced push notification to mobile devices of user
|
||||
*
|
||||
* @param userId the cloud user id of the recipient
|
||||
* @param message the body of the notification
|
||||
* @param icon name for the notification
|
||||
* @param severity category for the notification
|
||||
*
|
||||
*/
|
||||
@ActionDoc(text = "Sends a push notification to mobile devices of user with userId")
|
||||
public static void sendNotification(String userId, String message, String icon, String severity) {
|
||||
logger.debug("sending notification '{}' to user {}", message, userId);
|
||||
if (cloudService != null) {
|
||||
cloudService.sendNotification(userId, message, icon, severity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a simple notification to log. Log notifications are not pushed to user
|
||||
* devices but are shown to all account users in notifications log.
|
||||
*
|
||||
* @param message the body of the notification
|
||||
*
|
||||
*/
|
||||
@ActionDoc(text = "Sends a log notification which is shown in notifications log to all account users")
|
||||
public static void sendLogNotification(String message) {
|
||||
sendLogNotification(message, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an advanced notification to log. Log notifications are not pushed to user
|
||||
* devices but are shown to all account users in notifications log.
|
||||
*
|
||||
* @param message the body of the notification
|
||||
* @param icon name for the notification
|
||||
* @param severity category for the notification
|
||||
*
|
||||
*/
|
||||
@ActionDoc(text = "Sends a log notification which is shown in notifications log to all account users")
|
||||
public static void sendLogNotification(String message, String icon, String severity) {
|
||||
logger.debug("sending log notification '{}'", message);
|
||||
if (cloudService != null) {
|
||||
cloudService.sendLogNotification(message, icon, severity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a simple broadcast notification. Broadcast notifications are pushed to all
|
||||
* mobile devices of all users of the account
|
||||
*
|
||||
* @param message the body of the notification
|
||||
*
|
||||
*/
|
||||
@ActionDoc(text = "Sends a broadcast notification to all mobile devices of all account users")
|
||||
public static void sendBroadcastNotification(String message) {
|
||||
sendBroadcastNotification(message, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an advanced broadcast notification. Broadcast notifications are pushed to all
|
||||
* mobile devices of all users of the account
|
||||
*
|
||||
* @param message the body of the notification
|
||||
* @param icon name for the notification
|
||||
* @param severity category for the notification
|
||||
*
|
||||
*/
|
||||
@ActionDoc(text = "Sends a push notification to mobile devices of user with userId")
|
||||
public static void sendBroadcastNotification(String message, String icon, String severity) {
|
||||
logger.debug("sending broadcast notification '{}' to all users", message);
|
||||
if (cloudService != null) {
|
||||
cloudService.sendBroadcastNotification(message, icon, severity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,629 @@
|
||||
/**
|
||||
* 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.io.openhabcloud.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Request.FailureListener;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Response.ContentListener;
|
||||
import org.eclipse.jetty.client.api.Response.HeadersListener;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.openhab.core.OpenHAB;
|
||||
import org.openhab.core.common.ThreadPoolManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.socket.client.IO;
|
||||
import io.socket.client.Manager;
|
||||
import io.socket.client.Socket;
|
||||
import io.socket.emitter.Emitter;
|
||||
import io.socket.engineio.client.Transport;
|
||||
|
||||
/**
|
||||
* This class provides communication between openHAB and the openHAB Cloud service.
|
||||
* It also implements async http proxy for serving requests from user to
|
||||
* openHAB through the openHAB Cloud. It uses Socket.IO connection to connect to
|
||||
* the openHAB Cloud service and Jetty Http client to send local http requests to
|
||||
* openHAB.
|
||||
*
|
||||
* @author Victor Belov - Initial contribution
|
||||
* @author Kai Kreuzer - migrated code to new Jetty client and ESH APIs
|
||||
*
|
||||
*/
|
||||
|
||||
public class CloudClient {
|
||||
/*
|
||||
* Logger for this class
|
||||
*/
|
||||
private Logger logger = LoggerFactory.getLogger(CloudClient.class);
|
||||
|
||||
/*
|
||||
* This variable holds base URL for the openHAB Cloud connections
|
||||
*/
|
||||
private final String baseURL;
|
||||
|
||||
/*
|
||||
* This variable holds openHAB's UUID for authenticating and connecting to the openHAB Cloud
|
||||
*/
|
||||
private final String uuid;
|
||||
|
||||
/*
|
||||
* This variable holds openHAB's secret for authenticating and connecting to the openHAB Cloud
|
||||
*/
|
||||
private final String secret;
|
||||
|
||||
/*
|
||||
* This variable holds local openHAB's base URL for connecting to the local openHAB instance
|
||||
*/
|
||||
private final String localBaseUrl;
|
||||
|
||||
/*
|
||||
* This variable holds instance of Jetty HTTP client to make requests to local openHAB
|
||||
*/
|
||||
private final HttpClient jettyClient;
|
||||
|
||||
/*
|
||||
* This hashmap holds HTTP requests to local openHAB which are currently running
|
||||
*/
|
||||
private Map<Integer, Request> runningRequests;
|
||||
|
||||
/*
|
||||
* This variable indicates if connection to the openHAB Cloud is currently in an established state
|
||||
*/
|
||||
private boolean isConnected;
|
||||
|
||||
/*
|
||||
* This variable holds version of local openHAB
|
||||
*/
|
||||
private String openHABVersion;
|
||||
|
||||
/*
|
||||
* This variable holds instance of Socket.IO client class which provides communication
|
||||
* with the openHAB Cloud
|
||||
*/
|
||||
private Socket socket;
|
||||
|
||||
/*
|
||||
* The protocol of the openHAB-cloud URL.
|
||||
*/
|
||||
private String protocol = "https";
|
||||
|
||||
/*
|
||||
* This variable holds instance of CloudClientListener which provides callbacks to communicate
|
||||
* certain events from the openHAB Cloud back to openHAB
|
||||
*/
|
||||
private CloudClientListener listener;
|
||||
private boolean remoteAccessEnabled;
|
||||
private Set<String> exposedItems;
|
||||
|
||||
/**
|
||||
* Constructor of CloudClient
|
||||
*
|
||||
* @param uuid openHAB's UUID to connect to the openHAB Cloud
|
||||
* @param secret openHAB's Secret to connect to the openHAB Cloud
|
||||
* @param remoteAccessEnabled Allow the openHAB Cloud to be used as a remote proxy
|
||||
* @param exposedItems Items that are made available to apps connected to the openHAB Cloud
|
||||
*/
|
||||
public CloudClient(HttpClient httpClient, String uuid, String secret, String baseURL, String localBaseUrl,
|
||||
boolean remoteAccessEnabled, Set<String> exposedItems) {
|
||||
this.uuid = uuid;
|
||||
this.secret = secret;
|
||||
this.baseURL = baseURL;
|
||||
this.localBaseUrl = localBaseUrl;
|
||||
this.remoteAccessEnabled = remoteAccessEnabled;
|
||||
this.exposedItems = exposedItems;
|
||||
runningRequests = new HashMap<>();
|
||||
this.jettyClient = httpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the openHAB Cloud
|
||||
*/
|
||||
|
||||
public void connect() {
|
||||
try {
|
||||
socket = IO.socket(baseURL);
|
||||
URL parsed = new URL(baseURL);
|
||||
protocol = parsed.getProtocol();
|
||||
} catch (URISyntaxException e) {
|
||||
logger.error("Error creating Socket.IO: {}", e.getMessage());
|
||||
} catch (MalformedURLException e) {
|
||||
logger.error("Error parsing baseURL to get protocol, assuming https. Error: {}", e.getMessage());
|
||||
}
|
||||
socket.io().on(Manager.EVENT_TRANSPORT, new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
logger.trace("Manager.EVENT_TRANSPORT");
|
||||
Transport transport = (Transport) args[0];
|
||||
transport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
logger.trace("Transport.EVENT_REQUEST_HEADERS");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, List<String>> headers = (Map<String, List<String>>) args[0];
|
||||
headers.put("uuid", Arrays.asList(uuid));
|
||||
headers.put("secret", Arrays.asList(secret));
|
||||
headers.put("openhabversion", Arrays.asList(OpenHAB.getVersion()));
|
||||
headers.put("clientversion", Arrays.asList(CloudService.clientVersion));
|
||||
headers.put("remoteaccess", Arrays.asList(((Boolean) remoteAccessEnabled).toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
logger.debug("Socket.IO connected");
|
||||
isConnected = true;
|
||||
onConnect();
|
||||
}
|
||||
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
logger.debug("Socket.IO disconnected");
|
||||
isConnected = false;
|
||||
onDisconnect();
|
||||
}
|
||||
}).on(Socket.EVENT_ERROR, new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
logger.error("Error connecting to the openHAB Cloud instance: {}", args[0]);
|
||||
}
|
||||
}).on("request", new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
onEvent("request", (JSONObject) args[0]);
|
||||
}
|
||||
}).on("cancel", new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
onEvent("cancel", (JSONObject) args[0]);
|
||||
}
|
||||
}).on("command", new Emitter.Listener() {
|
||||
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
onEvent("command", (JSONObject) args[0]);
|
||||
}
|
||||
});
|
||||
socket.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for socket.io client which is called when connection is established
|
||||
*/
|
||||
|
||||
public void onConnect() {
|
||||
logger.info("Connected to the openHAB Cloud service (UUID = {}, base URL = {})", this.uuid, this.localBaseUrl);
|
||||
isConnected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for socket.io client which is called when disconnect occurs
|
||||
*/
|
||||
|
||||
public void onDisconnect() {
|
||||
logger.info("Disconnected from the openHAB Cloud service (UUID = {}, base URL = {})", this.uuid,
|
||||
this.localBaseUrl);
|
||||
isConnected = false;
|
||||
// And clean up the list of running requests
|
||||
if (runningRequests != null) {
|
||||
runningRequests.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for socket.io client which is called when an error occurs
|
||||
*/
|
||||
|
||||
public void onError(IOException error) {
|
||||
logger.debug("{}", error.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for socket.io client which is called when a message is received
|
||||
*/
|
||||
|
||||
public void onEvent(String event, JSONObject data) {
|
||||
logger.debug("on(): {}", event);
|
||||
if ("command".equals(event)) {
|
||||
handleCommandEvent(data);
|
||||
return;
|
||||
}
|
||||
if (remoteAccessEnabled) {
|
||||
if ("request".equals(event)) {
|
||||
handleRequestEvent(data);
|
||||
} else if ("cancel".equals(event)) {
|
||||
handleCancelEvent(data);
|
||||
} else {
|
||||
logger.warn("Unsupported event from openHAB Cloud: {}", event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRequestEvent(JSONObject data) {
|
||||
try {
|
||||
// Get unique request Id
|
||||
int requestId = data.getInt("id");
|
||||
logger.debug("Got request {}", requestId);
|
||||
// Get request path
|
||||
String requestPath = data.getString("path");
|
||||
// Get request method
|
||||
String requestMethod = data.getString("method");
|
||||
// Get request body
|
||||
String requestBody = data.getString("body");
|
||||
// Get JSONObject for request headers
|
||||
JSONObject requestHeadersJson = data.getJSONObject("headers");
|
||||
logger.debug("{}", requestHeadersJson.toString());
|
||||
// Get JSONObject for request query parameters
|
||||
JSONObject requestQueryJson = data.getJSONObject("query");
|
||||
// Create URI builder with base request URI of openHAB and path from request
|
||||
String newPath = URIUtil.addPaths(localBaseUrl, requestPath);
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<String> queryIterator = requestQueryJson.keys();
|
||||
// Add query parameters to URI builder, if any
|
||||
newPath += "?";
|
||||
while (queryIterator.hasNext()) {
|
||||
String queryName = queryIterator.next();
|
||||
newPath += queryName;
|
||||
newPath += "=";
|
||||
newPath += URLEncoder.encode(requestQueryJson.getString(queryName), "UTF-8");
|
||||
if (queryIterator.hasNext()) {
|
||||
newPath += "&";
|
||||
}
|
||||
}
|
||||
// Finally get the future request URI
|
||||
URI requestUri = new URI(newPath);
|
||||
// All preparations which are common for different methods are done
|
||||
// Now perform the request to openHAB
|
||||
// If method is GET
|
||||
logger.debug("Request method is {}", requestMethod);
|
||||
Request request = jettyClient.newRequest(requestUri);
|
||||
setRequestHeaders(request, requestHeadersJson);
|
||||
String proto = protocol;
|
||||
if (data.has("protocol")) {
|
||||
proto = data.getString("protocol");
|
||||
}
|
||||
request.header("X-Forwarded-Proto", proto);
|
||||
|
||||
if (requestMethod.equals("GET")) {
|
||||
request.method(HttpMethod.GET);
|
||||
} else if (requestMethod.equals("POST")) {
|
||||
request.method(HttpMethod.POST);
|
||||
request.content(new BytesContentProvider(requestBody.getBytes()));
|
||||
} else if (requestMethod.equals("PUT")) {
|
||||
request.method(HttpMethod.PUT);
|
||||
request.content(new BytesContentProvider(requestBody.getBytes()));
|
||||
} else {
|
||||
// TODO: Reject unsupported methods
|
||||
logger.warn("Unsupported request method {}", requestMethod);
|
||||
return;
|
||||
}
|
||||
ResponseListener listener = new ResponseListener(requestId);
|
||||
request.onResponseHeaders(listener).onResponseContent(listener).onRequestFailure(listener).send(listener);
|
||||
// If successfully submitted request to http client, add it to the list of currently
|
||||
// running requests to be able to cancel it if needed
|
||||
runningRequests.put(requestId, request);
|
||||
} catch (JSONException | IOException | URISyntaxException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void setRequestHeaders(Request request, JSONObject requestHeadersJson) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<String> headersIterator = requestHeadersJson.keys();
|
||||
// Convert JSONObject of headers into Header ArrayList
|
||||
while (headersIterator.hasNext()) {
|
||||
String headerName = headersIterator.next();
|
||||
String headerValue;
|
||||
try {
|
||||
headerValue = requestHeadersJson.getString(headerName);
|
||||
logger.debug("Jetty set header {} = {}", headerName, headerValue);
|
||||
if (!headerName.equalsIgnoreCase("Content-Length")) {
|
||||
request.header(headerName, headerValue);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
logger.warn("Error processing request headers: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCancelEvent(JSONObject data) {
|
||||
try {
|
||||
int requestId = data.getInt("id");
|
||||
logger.debug("Received cancel for request {}", requestId);
|
||||
// Find and abort running request
|
||||
if (runningRequests.containsKey(requestId)) {
|
||||
Request request = runningRequests.get(requestId);
|
||||
request.abort(new InterruptedException());
|
||||
runningRequests.remove(requestId);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCommandEvent(JSONObject data) {
|
||||
String itemName = data.getString("item");
|
||||
if (exposedItems.contains(itemName)) {
|
||||
try {
|
||||
logger.debug("Received command {} for item {}.", data.getString("command"), itemName);
|
||||
if (this.listener != null) {
|
||||
this.listener.sendCommand(itemName, data.getString("command"));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.warn("Received command from openHAB Cloud for item '{}', which is not exposed.", itemName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sends notification to the openHAB Cloud
|
||||
*
|
||||
* @param userId openHAB Cloud user id
|
||||
* @param message notification message text
|
||||
* @param icon name of the icon for this notification
|
||||
* @param severity severity name for this notification
|
||||
*
|
||||
*/
|
||||
public void sendNotification(String userId, String message, String icon, String severity) {
|
||||
if (isConnected()) {
|
||||
JSONObject notificationMessage = new JSONObject();
|
||||
try {
|
||||
notificationMessage.put("userId", userId);
|
||||
notificationMessage.put("message", message);
|
||||
notificationMessage.put("icon", icon);
|
||||
notificationMessage.put("severity", severity);
|
||||
socket.emit("notification", notificationMessage);
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.debug("No connection, notification is not sent");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sends log notification to the openHAB Cloud
|
||||
*
|
||||
* @param message notification message text
|
||||
* @param icon name of the icon for this notification
|
||||
* @param severity severity name for this notification
|
||||
*
|
||||
*/
|
||||
public void sendLogNotification(String message, String icon, String severity) {
|
||||
if (isConnected()) {
|
||||
JSONObject notificationMessage = new JSONObject();
|
||||
try {
|
||||
notificationMessage.put("message", message);
|
||||
notificationMessage.put("icon", icon);
|
||||
notificationMessage.put("severity", severity);
|
||||
socket.emit("lognotification", notificationMessage);
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.debug("No connection, notification is not sent");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sends broadcast notification to the openHAB Cloud
|
||||
*
|
||||
* @param message notification message text
|
||||
* @param icon name of the icon for this notification
|
||||
* @param severity severity name for this notification
|
||||
*
|
||||
*/
|
||||
public void sendBroadcastNotification(String message, String icon, String severity) {
|
||||
if (isConnected()) {
|
||||
JSONObject notificationMessage = new JSONObject();
|
||||
try {
|
||||
notificationMessage.put("message", message);
|
||||
notificationMessage.put("icon", icon);
|
||||
notificationMessage.put("severity", severity);
|
||||
socket.emit("broadcastnotification", notificationMessage);
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.debug("No connection, notification is not sent");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send item update to openHAB Cloud
|
||||
*
|
||||
* @param itemName the name of the item
|
||||
* @param itemState updated item state
|
||||
*
|
||||
*/
|
||||
public void sendItemUpdate(String itemName, String itemState) {
|
||||
if (isConnected()) {
|
||||
logger.debug("Sending update '{}' for item '{}'", itemState, itemName);
|
||||
JSONObject itemUpdateMessage = new JSONObject();
|
||||
try {
|
||||
itemUpdateMessage.put("itemName", itemName);
|
||||
itemUpdateMessage.put("itemStatus", itemState);
|
||||
socket.emit("itemupdate", itemUpdateMessage);
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.debug("No connection, Item update is not sent");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if openHAB Cloud connection is active
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from openHAB Cloud
|
||||
*/
|
||||
public void shutdown() {
|
||||
logger.info("Shutting down openHAB Cloud service connection");
|
||||
socket.disconnect();
|
||||
}
|
||||
|
||||
public String getOpenHABVersion() {
|
||||
return openHABVersion;
|
||||
}
|
||||
|
||||
public void setOpenHABVersion(String openHABVersion) {
|
||||
this.openHABVersion = openHABVersion;
|
||||
}
|
||||
|
||||
public void setListener(CloudClientListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/*
|
||||
* An internal class which forwards response headers and data back to the openHAB Cloud
|
||||
*/
|
||||
private class ResponseListener
|
||||
implements Response.CompleteListener, HeadersListener, ContentListener, FailureListener {
|
||||
|
||||
private static final String THREADPOOL_OPENHABCLOUD = "openhabcloud";
|
||||
private int mRequestId;
|
||||
private boolean mHeadersSent = false;
|
||||
|
||||
public ResponseListener(int requestId) {
|
||||
mRequestId = requestId;
|
||||
}
|
||||
|
||||
private JSONObject getJSONHeaders(HttpFields httpFields) {
|
||||
JSONObject headersJSON = new JSONObject();
|
||||
try {
|
||||
for (HttpField field : httpFields) {
|
||||
headersJSON.put(field.getName(), field.getValue());
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
logger.warn("Error forming response headers: {}", e.getMessage());
|
||||
}
|
||||
return headersJSON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Result result) {
|
||||
// Remove this request from list of running requests
|
||||
runningRequests.remove(mRequestId);
|
||||
if ((result != null && result.isFailed())
|
||||
&& (result.getResponse() != null && result.getResponse().getStatus() != HttpStatus.OK_200)) {
|
||||
if (result.getFailure() != null) {
|
||||
logger.warn("Jetty request {} failed: {}", mRequestId, result.getFailure().getMessage());
|
||||
}
|
||||
if (result.getRequestFailure() != null) {
|
||||
logger.warn("Request Failure: {}", result.getRequestFailure().getMessage());
|
||||
}
|
||||
if (result.getResponseFailure() != null) {
|
||||
logger.warn("Response Failure: {}", result.getResponseFailure().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* What is this? In some cases where latency is very low the myopenhab service
|
||||
* can receive responseFinished before the headers or content are received and I
|
||||
* cannot find another workaround to prevent it.
|
||||
*/
|
||||
ThreadPoolManager.getScheduledPool(THREADPOOL_OPENHABCLOUD).schedule(() -> {
|
||||
JSONObject responseJson = new JSONObject();
|
||||
try {
|
||||
responseJson.put("id", mRequestId);
|
||||
socket.emit("responseFinished", responseJson);
|
||||
logger.debug("Finished responding to request {}", mRequestId);
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
}, 1, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onFailure(Request request, Throwable failure) {
|
||||
JSONObject responseJson = new JSONObject();
|
||||
try {
|
||||
responseJson.put("id", mRequestId);
|
||||
responseJson.put("responseStatusText", "openHAB connection error: " + failure.getMessage());
|
||||
socket.emit("responseError", responseJson);
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContent(Response response, ByteBuffer content) {
|
||||
logger.debug("Jetty received response content of size {}", String.valueOf(content.remaining()));
|
||||
JSONObject responseJson = new JSONObject();
|
||||
try {
|
||||
responseJson.put("id", mRequestId);
|
||||
responseJson.put("body", BufferUtil.toArray(content));
|
||||
socket.emit("responseContentBinary", responseJson);
|
||||
logger.debug("Sent content to request {}", mRequestId);
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(Response response) {
|
||||
if (!mHeadersSent) {
|
||||
logger.debug("Jetty finished receiving response header");
|
||||
JSONObject responseJson = new JSONObject();
|
||||
mHeadersSent = true;
|
||||
try {
|
||||
responseJson.put("id", mRequestId);
|
||||
responseJson.put("headers", getJSONHeaders(response.getHeaders()));
|
||||
responseJson.put("responseStatusCode", response.getStatus());
|
||||
responseJson.put("responseStatusText", "OK");
|
||||
socket.emit("responseHeader", responseJson);
|
||||
logger.debug("Sent headers to request {}", mRequestId);
|
||||
logger.debug("{}", responseJson.toString());
|
||||
} catch (JSONException e) {
|
||||
logger.debug("{}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
// We should not send headers for the second time...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.io.openhabcloud.internal;
|
||||
|
||||
/**
|
||||
* This interface provides callbacks from CloudClient
|
||||
*
|
||||
* @author Victor Belov - Initial contribution
|
||||
* @author Kai Kreuzer - migrated code to ESH APIs
|
||||
*
|
||||
*/
|
||||
|
||||
public interface CloudClientListener {
|
||||
/**
|
||||
* This method receives command for an item from the openHAB Cloud client and should post it
|
||||
* into openHAB
|
||||
*
|
||||
* @param item the {@link String} containing item name
|
||||
* @param command the {@link String} containing a command
|
||||
*/
|
||||
public void sendCommand(String item, String command);
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* 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.io.openhabcloud.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.core.OpenHAB;
|
||||
import org.openhab.core.config.core.ConfigConstants;
|
||||
import org.openhab.core.config.core.ConfigurableService;
|
||||
import org.openhab.core.events.Event;
|
||||
import org.openhab.core.events.EventFilter;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.events.EventSubscriber;
|
||||
import org.openhab.core.id.InstanceUUID;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemNotFoundException;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.items.events.ItemEventFactory;
|
||||
import org.openhab.core.items.events.ItemStateEvent;
|
||||
import org.openhab.core.library.items.RollershutterItem;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.model.script.engine.action.ActionService;
|
||||
import org.openhab.core.net.HttpServiceUtil;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.TypeParser;
|
||||
import org.openhab.io.openhabcloud.NotificationAction;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.Constants;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.component.annotations.ReferenceCardinality;
|
||||
import org.osgi.service.component.annotations.ReferencePolicy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class starts the cloud connection service and implements interface to communicate with the cloud.
|
||||
*
|
||||
* @author Victor Belov - Initial contribution
|
||||
* @author Kai Kreuzer - migrated code to new Jetty client and ESH APIs
|
||||
*/
|
||||
@Component(immediate = true, service = { EventSubscriber.class,
|
||||
ActionService.class }, configurationPid = "org.openhab.openhabcloud", property = {
|
||||
Constants.SERVICE_PID + "=org.openhab.openhabcloud",
|
||||
ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=io:openhabcloud",
|
||||
ConfigurableService.SERVICE_PROPERTY_LABEL + "=openHAB Cloud",
|
||||
ConfigurableService.SERVICE_PROPERTY_CATEGORY + "=io" })
|
||||
public class CloudService implements ActionService, CloudClientListener, EventSubscriber {
|
||||
|
||||
private static final String CFG_EXPOSE = "expose";
|
||||
private static final String CFG_BASE_URL = "baseURL";
|
||||
private static final String CFG_MODE = "mode";
|
||||
private static final String SECRET_FILE_NAME = "openhabcloud" + File.separator + "secret";
|
||||
private static final String DEFAULT_URL = "https://myopenhab.org/";
|
||||
private static final int DEFAULT_LOCAL_OPENHAB_MAX_CONCURRENT_REQUESTS = 200;
|
||||
private static final int DEFAULT_LOCAL_OPENHAB_REQUEST_TIMEOUT = 30000;
|
||||
private static final String HTTPCLIENT_NAME = "openhabcloud";
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(CloudService.class);
|
||||
|
||||
public static String clientVersion = null;
|
||||
private CloudClient cloudClient;
|
||||
private String cloudBaseUrl = null;
|
||||
private HttpClient httpClient;
|
||||
protected ItemRegistry itemRegistry = null;
|
||||
protected EventPublisher eventPublisher = null;
|
||||
|
||||
private boolean remoteAccessEnabled = true;
|
||||
private Set<String> exposedItems = null;
|
||||
private int localPort;
|
||||
|
||||
public CloudService() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sends notification message to mobile app through the openHAB Cloud service
|
||||
*
|
||||
* @param userId the {@link String} containing the openHAB Cloud user id to send message to
|
||||
* @param message the {@link String} containing a message to send to specified user id
|
||||
* @param icon the {@link String} containing a name of the icon to be used with this notification
|
||||
* @param severity the {@link String} containing severity (good, info, warning, error) of notification
|
||||
*/
|
||||
public void sendNotification(String userId, String message, String icon, String severity) {
|
||||
logger.debug("Sending message '{}' to user id {}", message, userId);
|
||||
cloudClient.sendNotification(userId, message, icon, severity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an advanced notification to log. Log notifications are not pushed to user
|
||||
* devices but are shown to all account users in notifications log
|
||||
*
|
||||
* @param message the {@link String} containing a message to send to specified user id
|
||||
* @param icon the {@link String} containing a name of the icon to be used with this notification
|
||||
* @param severity the {@link String} containing severity (good, info, warning, error) of notification
|
||||
*/
|
||||
public void sendLogNotification(String message, String icon, String severity) {
|
||||
logger.debug("Sending log message '{}'", message);
|
||||
cloudClient.sendLogNotification(message, icon, severity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a broadcast notification. Broadcast notifications are pushed to all
|
||||
* mobile devices of all users of the account
|
||||
*
|
||||
* @param message the {@link String} containing a message to send to specified user id
|
||||
* @param icon the {@link String} containing a name of the icon to be used with this notification
|
||||
* @param severity the {@link String} containing severity (good, info, warning, error) of notification
|
||||
*/
|
||||
public void sendBroadcastNotification(String message, String icon, String severity) {
|
||||
logger.debug("Sending broadcast message '{}' to all users", message);
|
||||
cloudClient.sendBroadcastNotification(message, icon, severity);
|
||||
}
|
||||
|
||||
@Activate
|
||||
protected void activate(BundleContext context, Map<String, ?> config) {
|
||||
clientVersion = StringUtils.substringBefore(context.getBundle().getVersion().toString(), ".qualifier");
|
||||
localPort = HttpServiceUtil.getHttpServicePort(context);
|
||||
if (localPort == -1) {
|
||||
logger.warn("openHAB Cloud connector not started, since no local HTTP port could be determined");
|
||||
} else {
|
||||
logger.debug("openHAB Cloud connector activated");
|
||||
checkJavaVersion();
|
||||
modified(config);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkJavaVersion() {
|
||||
String version = System.getProperty("java.version");
|
||||
if (version.charAt(2) == '8') {
|
||||
// we are on Java 8, let's check the update
|
||||
String update = version.substring(version.indexOf('_') + 1);
|
||||
try {
|
||||
Integer uVersion = Integer.valueOf(update);
|
||||
if (uVersion < 101) {
|
||||
logger.warn(
|
||||
"You are running Java {} - the openhab Cloud connection requires at least Java 1.8.0_101, if your cloud server uses Let's Encrypt certificates!",
|
||||
version);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Could not determine update version of java {}", version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
protected void deactivate() {
|
||||
logger.debug("openHAB Cloud connector deactivated");
|
||||
cloudClient.shutdown();
|
||||
try {
|
||||
httpClient.stop();
|
||||
} catch (Exception e) {
|
||||
logger.debug("Could not stop Jetty http client", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Modified
|
||||
protected void modified(Map<String, ?> config) {
|
||||
if (config != null && config.get(CFG_MODE) != null) {
|
||||
remoteAccessEnabled = "remote".equals(config.get(CFG_MODE));
|
||||
} else {
|
||||
logger.debug("remoteAccessEnabled is not set, keeping value '{}'", remoteAccessEnabled);
|
||||
}
|
||||
|
||||
if (config.get(CFG_BASE_URL) != null) {
|
||||
cloudBaseUrl = (String) config.get(CFG_BASE_URL);
|
||||
} else {
|
||||
cloudBaseUrl = DEFAULT_URL;
|
||||
}
|
||||
|
||||
exposedItems = new HashSet<>();
|
||||
Object expCfg = config.get(CFG_EXPOSE);
|
||||
if (expCfg instanceof String) {
|
||||
String value = (String) expCfg;
|
||||
while (value.startsWith("[")) {
|
||||
value = value.substring(1);
|
||||
}
|
||||
while (value.endsWith("]")) {
|
||||
value = value.substring(0, value.length() - 1);
|
||||
}
|
||||
for (String itemName : Arrays.asList((value).split(","))) {
|
||||
exposedItems.add(itemName.trim());
|
||||
}
|
||||
} else if (expCfg instanceof Iterable) {
|
||||
for (Object entry : ((Iterable<?>) expCfg)) {
|
||||
exposedItems.add(entry.toString());
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("UUID = {}, secret = {}", InstanceUUID.get(), getSecret());
|
||||
|
||||
if (cloudClient != null) {
|
||||
cloudClient.shutdown();
|
||||
}
|
||||
|
||||
httpClient.setMaxConnectionsPerDestination(DEFAULT_LOCAL_OPENHAB_MAX_CONCURRENT_REQUESTS);
|
||||
httpClient.setConnectTimeout(DEFAULT_LOCAL_OPENHAB_REQUEST_TIMEOUT);
|
||||
httpClient.setFollowRedirects(false);
|
||||
if (!httpClient.isRunning()) {
|
||||
try {
|
||||
httpClient.start();
|
||||
} catch (Exception e) {
|
||||
logger.error("Could not start Jetty http client", e);
|
||||
}
|
||||
}
|
||||
|
||||
String localBaseUrl = "http://localhost:" + localPort;
|
||||
cloudClient = new CloudClient(httpClient, InstanceUUID.get(), getSecret(), cloudBaseUrl, localBaseUrl,
|
||||
remoteAccessEnabled, exposedItems);
|
||||
cloudClient.setOpenHABVersion(OpenHAB.getVersion());
|
||||
cloudClient.connect();
|
||||
cloudClient.setListener(this);
|
||||
NotificationAction.cloudService = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActionClassName() {
|
||||
return NotificationAction.class.getCanonicalName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getActionClass() {
|
||||
return NotificationAction.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the first line from specified file
|
||||
*/
|
||||
|
||||
private String readFirstLine(File file) {
|
||||
List<String> lines = null;
|
||||
try (InputStream fis = new FileInputStream(file)) {
|
||||
lines = IOUtils.readLines(fis);
|
||||
} catch (IOException ioe) {
|
||||
// no exception handling - we just return the empty String
|
||||
}
|
||||
return lines != null && !lines.isEmpty() ? lines.get(0) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a String to a specified file
|
||||
*/
|
||||
|
||||
private void writeFile(File file, String content) {
|
||||
// create intermediary directories
|
||||
file.getParentFile().mkdirs();
|
||||
try (OutputStream fos = new FileOutputStream(file)) {
|
||||
IOUtils.write(content, fos);
|
||||
logger.debug("Created file '{}' with content '{}'", file.getAbsolutePath(), content);
|
||||
} catch (FileNotFoundException e) {
|
||||
logger.error("Couldn't create file '{}'.", file.getPath(), e);
|
||||
} catch (IOException e) {
|
||||
logger.error("Couldn't write to file '{}'.", file.getPath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a random secret and writes it to the <code>userdata/openhabcloud</code>
|
||||
* directory. An existing <code>secret</code> file won't be overwritten.
|
||||
* Returns either existing secret from the file or newly created secret.
|
||||
*/
|
||||
private String getSecret() {
|
||||
File file = new File(ConfigConstants.getUserDataFolder() + File.separator + SECRET_FILE_NAME);
|
||||
String newSecretString = "";
|
||||
|
||||
if (!file.exists()) {
|
||||
newSecretString = RandomStringUtils.randomAlphanumeric(20);
|
||||
logger.debug("New secret = {}", newSecretString);
|
||||
writeFile(file, newSecretString);
|
||||
} else {
|
||||
newSecretString = readFirstLine(file);
|
||||
logger.debug("Using secret at '{}' with content '{}'", file.getAbsolutePath(), newSecretString);
|
||||
}
|
||||
|
||||
return newSecretString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCommand(String itemName, String commandString) {
|
||||
try {
|
||||
if (itemRegistry != null) {
|
||||
Item item = itemRegistry.getItem(itemName);
|
||||
Command command = null;
|
||||
if (item != null) {
|
||||
if (this.eventPublisher != null) {
|
||||
if ("toggle".equalsIgnoreCase(commandString)
|
||||
&& (item instanceof SwitchItem || item instanceof RollershutterItem)) {
|
||||
if (OnOffType.ON.equals(item.getStateAs(OnOffType.class))) {
|
||||
command = OnOffType.OFF;
|
||||
}
|
||||
if (OnOffType.OFF.equals(item.getStateAs(OnOffType.class))) {
|
||||
command = OnOffType.ON;
|
||||
}
|
||||
if (UpDownType.UP.equals(item.getStateAs(UpDownType.class))) {
|
||||
command = UpDownType.DOWN;
|
||||
}
|
||||
if (UpDownType.DOWN.equals(item.getStateAs(UpDownType.class))) {
|
||||
command = UpDownType.UP;
|
||||
}
|
||||
} else {
|
||||
command = TypeParser.parseCommand(item.getAcceptedCommandTypes(), commandString);
|
||||
}
|
||||
if (command != null) {
|
||||
logger.debug("Received command '{}' for item '{}'", commandString, itemName);
|
||||
this.eventPublisher.post(ItemEventFactory.createCommandEvent(itemName, command));
|
||||
} else {
|
||||
logger.warn("Received invalid command '{}' for item '{}'", commandString, itemName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn("Received command '{}' for non-existent item '{}'", commandString, itemName);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} catch (ItemNotFoundException e) {
|
||||
logger.warn("Received command for a non-existent item '{}'", itemName);
|
||||
}
|
||||
}
|
||||
|
||||
@Reference
|
||||
protected void setHttpClientFactory(HttpClientFactory httpClientFactory) {
|
||||
this.httpClient = httpClientFactory.createHttpClient(HTTPCLIENT_NAME);
|
||||
this.httpClient.setStopTimeout(0);
|
||||
}
|
||||
|
||||
protected void unsetHttpClientFactory(HttpClientFactory httpClientFactory) {
|
||||
this.httpClient = null;
|
||||
}
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC)
|
||||
public void setItemRegistry(ItemRegistry itemRegistry) {
|
||||
this.itemRegistry = itemRegistry;
|
||||
}
|
||||
|
||||
public void unsetItemRegistry(ItemRegistry itemRegistry) {
|
||||
this.itemRegistry = null;
|
||||
}
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC)
|
||||
public void setEventPublisher(EventPublisher eventPublisher) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
public void unsetEventPublisher(EventPublisher eventPublisher) {
|
||||
this.eventPublisher = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSubscribedEventTypes() {
|
||||
return Collections.singleton(ItemStateEvent.TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventFilter getEventFilter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Event event) {
|
||||
ItemStateEvent ise = (ItemStateEvent) event;
|
||||
if (exposedItems != null && exposedItems.contains(ise.getItemName())) {
|
||||
cloudClient.sendItemUpdate(ise.getItemName(), ise.getItemState().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
<config-description uri="io:openhabcloud">
|
||||
<parameter name="mode" type="text" required="true">
|
||||
<label>Mode</label>
|
||||
<description>What features of the openHAB Cloud service should be used.</description>
|
||||
<options>
|
||||
<option value="notification">Notifications</option>
|
||||
<option value="remote">Notifications & Remote Access</option>
|
||||
</options>
|
||||
<default>remote</default>
|
||||
</parameter>
|
||||
<parameter name="expose" type="text" required="false" multiple="true">
|
||||
<label>Items to Expose</label>
|
||||
<description>List of items that are made accessible to IFTTT and similar services.</description>
|
||||
<context>item</context>
|
||||
</parameter>
|
||||
<parameter name="baseURL" type="text" required="false">
|
||||
<label>Base URL</label>
|
||||
<description>Base URL for the openHAB Cloud server</description>
|
||||
<default>https://myopenhab.org/</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
||||
Reference in New Issue
Block a user