From 4b91bfcfa06126955707308da8a6d9768a11c3ae Mon Sep 17 00:00:00 2001 From: miloit Date: Tue, 29 Sep 2020 19:48:11 +0200 Subject: [PATCH] [yioremote] Fixed reconnection and thing creation issues (#8596) Signed-off-by: Michael Loercher --- .../org.openhab.binding.yioremote/README.md | 12 +- .../internal/YIOremoteBindingConstants.java | 6 +- .../internal/YIOremoteDockHandler.java | 110 ++++++++++++++---- .../yioremote/internal/utils/Websocket.java | 28 ++++- .../internal/utils/WebsocketInterface.java | 4 +- 5 files changed, 128 insertions(+), 32 deletions(-) diff --git a/bundles/org.openhab.binding.yioremote/README.md b/bundles/org.openhab.binding.yioremote/README.md index 673ef6409..539009f45 100644 --- a/bundles/org.openhab.binding.yioremote/README.md +++ b/bundles/org.openhab.binding.yioremote/README.md @@ -8,7 +8,7 @@ Since this binding allows actual you to trigger IR send/receive actions on YIO D ## Supported Things -* Thing Type ID: `yioremotedock` +* Thing Type ID: `yioRemoteDock` The following are the configurations available to each of the bridges/things: @@ -41,7 +41,7 @@ rule "yioremote Action Example" when ... then - val actions = getActions("yioremote", "yioremote:yioremotedock:livingroom") + val actions = getActions("yioremote", "yioremote:yioRemoteDock:livingroom") if (actions === null) { ...... @@ -58,22 +58,20 @@ end .things ``` -yioremote:yioremotedock:livingroom [ host="192.168.178.21", accesstoken="0" ] +yioremote:yioRemoteDock:livingroom [ host="xxx.xxx.xxx.xxx", accesstoken="0" ] ``` .items ``` -String sendircode "IR CODE [%s]" {channel="yioremote:yioremotedock:livingroom:input# sendircode"} -Switch receiverswitch "IR recieving switch" {channel="yioremote:yioremotedock:livingroom:input# receiverswitch"} -String status "YIO Dock status[%s]" {channel="yioremote:yioremotedock:livingroom:output# status"} +Switch receiverswitch "IR recieving switch" {channel="yioremote:yioRemoteDock:livingroom:input# receiverswitch"} +String status "YIO Dock status[%s]" {channel="yioremote:yioRemoteDock:livingroom:output# status"} ``` .sitemap ``` sitemap Basic label="YIO Dock" { - Text item= sendircode Switch item= receiverswitch Text item= status } diff --git a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteBindingConstants.java b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteBindingConstants.java index caef352e8..66ef1cc7e 100644 --- a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteBindingConstants.java +++ b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteBindingConstants.java @@ -33,7 +33,9 @@ public class YIOremoteBindingConstants { AUTHENTICATION_FAILED, AUTHENTICATION_COMPLETE, CONNECTION_FAILED, - CONNECTION_ESTABLISHED; + CONNECTION_ESTABLISHED, + COMMUNICATION_ERROR, + RECONNECTION_PROCESS; } public static enum YioRemoteMessages { @@ -45,7 +47,7 @@ public class YIOremoteBindingConstants { } // List of all Thing Type UIDs - public static final ThingTypeUID THING_TYPE_YIOREMOTEDOCK = new ThingTypeUID(BINDING_ID, "yioremotedock"); + public static final ThingTypeUID THING_TYPE_YIOREMOTEDOCK = new ThingTypeUID(BINDING_ID, "yioRemoteDock"); // List of all Channel Groups Group Channel ids public static final String GROUP_INPUT = "input"; diff --git a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteDockHandler.java b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteDockHandler.java index 6b1f45187..c90a2cbaa 100644 --- a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteDockHandler.java +++ b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/YIOremoteDockHandler.java @@ -69,6 +69,7 @@ public class YIOremoteDockHandler extends BaseThingHandler { private @Nullable URI websocketAddress; private YioRemoteDockHandleStatus yioRemoteDockActualStatus = YioRemoteDockHandleStatus.UNINITIALIZED_STATE; private @Nullable Future webSocketPollingJob; + private @Nullable Future webSocketReconnectionPollingJob; public String receivedMessage = ""; private JsonObject recievedJson = new JsonObject(); private boolean heartBeat = false; @@ -119,7 +120,10 @@ public class YIOremoteDockHandler extends BaseThingHandler { switch (yioRemoteDockActualStatus) { case CONNECTION_ESTABLISHED: case AUTHENTICATION_PROCESS: - authenticate(); + authenticateWebsocket(); + break; + case COMMUNICATION_ERROR: + reconnectWebsocket(); break; default: break; @@ -132,13 +136,15 @@ public class YIOremoteDockHandler extends BaseThingHandler { } @Override - public void onError() { - if (webSocketPollingJob != null) { - webSocketPollingJob.cancel(true); - } + public void onClose() { + reconnectWebsocket(); + } + + @Override + public void onError(Throwable cause) { + yioRemoteDockActualStatus = YioRemoteDockHandleStatus.COMMUNICATION_ERROR; updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Connection lost no ping from YIO DOCK"); - updateState(GROUP_OUTPUT, STATUS_STRING_CHANNEL, UnDefType.UNDEF); + "Communication lost no ping from YIO DOCK"); } }); @@ -174,8 +180,9 @@ public class YIOremoteDockHandler extends BaseThingHandler { heartBeat = true; success = true; } else { - if (irCodeSendHandler.getCode().equalsIgnoreCase("\"0;0x0;0;0\"")) { + if (irCodeSendHandler.getCode().equalsIgnoreCase("0;0x0;0;0")) { logger.debug("Send heartBeat Code success"); + receivedStatus = "Send heartBeat Code success"; } else { receivedStatus = "Send IR Code failure"; } @@ -242,8 +249,12 @@ public class YIOremoteDockHandler extends BaseThingHandler { @Override public void dispose() { - if (webSocketPollingJob != null) { - webSocketPollingJob.cancel(true); + disposeWebsocketPollingJob(); + if (webSocketReconnectionPollingJob != null) { + if (!webSocketReconnectionPollingJob.isCancelled() && webSocketReconnectionPollingJob != null) { + webSocketReconnectionPollingJob.cancel(true); + } + webSocketReconnectionPollingJob = null; } } @@ -287,7 +298,7 @@ public class YIOremoteDockHandler extends BaseThingHandler { updateState(id, new StringType(value)); } - private void authenticate() { + private void authenticateWebsocket() { switch (yioRemoteDockActualStatus) { case CONNECTION_ESTABLISHED: authenticationMessageHandler.setToken(localConfig.accessToken); @@ -298,13 +309,15 @@ public class YIOremoteDockHandler extends BaseThingHandler { if (authenticationOk) { yioRemoteDockActualStatus = YioRemoteDockHandleStatus.AUTHENTICATION_COMPLETE; updateStatus(ThingStatus.ONLINE); - webSocketPollingJob = scheduler.scheduleWithFixedDelay(this::pollingWebsocket, 0, 30, + webSocketPollingJob = scheduler.scheduleWithFixedDelay(this::pollingWebsocketJob, 0, 150, TimeUnit.SECONDS); } else { yioRemoteDockActualStatus = YioRemoteDockHandleStatus.AUTHENTICATION_FAILED; } break; default: + disposeWebsocketPollingJob(); + yioRemoteDockActualStatus = YioRemoteDockHandleStatus.COMMUNICATION_ERROR; updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection lost no ping from YIO DOCK"); updateState(GROUP_OUTPUT, STATUS_STRING_CHANNEL, UnDefType.UNDEF); @@ -312,7 +325,16 @@ public class YIOremoteDockHandler extends BaseThingHandler { } } - private void pollingWebsocket() { + private void disposeWebsocketPollingJob() { + if (webSocketPollingJob != null) { + if (!webSocketPollingJob.isCancelled() && webSocketPollingJob != null) { + webSocketPollingJob.cancel(true); + } + webSocketPollingJob = null; + } + } + + private void pollingWebsocketJob() { switch (yioRemoteDockActualStatus) { case AUTHENTICATION_COMPLETE: if (getAndResetHeartbeat()) { @@ -321,19 +343,16 @@ public class YIOremoteDockHandler extends BaseThingHandler { logger.debug("heartBeat ok"); sendMessage(YioRemoteMessages.HEARTBEAT_MESSAGE, ""); } else { - yioRemoteDockActualStatus = YioRemoteDockHandleStatus.CONNECTION_FAILED; + yioRemoteDockActualStatus = YioRemoteDockHandleStatus.COMMUNICATION_ERROR; updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection lost no ping from YIO DOCK"); - updateState(GROUP_OUTPUT, STATUS_STRING_CHANNEL, UnDefType.UNDEF); - if (webSocketPollingJob != null) { - webSocketPollingJob.cancel(true); - } + disposeWebsocketPollingJob(); + reconnectWebsocket(); } break; default: - if (webSocketPollingJob != null) { - webSocketPollingJob.cancel(true); - } + disposeWebsocketPollingJob(); + yioRemoteDockActualStatus = YioRemoteDockHandleStatus.COMMUNICATION_ERROR; updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Connection lost no ping from YIO DOCK"); updateState(GROUP_OUTPUT, STATUS_STRING_CHANNEL, UnDefType.UNDEF); @@ -347,6 +366,55 @@ public class YIOremoteDockHandler extends BaseThingHandler { return result; } + public void reconnectWebsocket() { + if (webSocketReconnectionPollingJob == null) { + webSocketReconnectionPollingJob = scheduler.scheduleWithFixedDelay(this::reconnectWebsocketJob, 0, 30, + TimeUnit.SECONDS); + } + } + + public void reconnectWebsocketJob() { + switch (yioRemoteDockActualStatus) { + case COMMUNICATION_ERROR: + logger.debug("Reconnecting YIORemoteHandler"); + try { + disposeWebsocketPollingJob(); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Connection lost no ping from YIO DOCK"); + yioremoteDockwebSocketClient.closeWebsocketSession(); + webSocketClient.stop(); + yioRemoteDockActualStatus = YioRemoteDockHandleStatus.RECONNECTION_PROCESS; + } catch (Exception e) { + logger.debug("Connection error {}", e.getMessage()); + } + try { + websocketAddress = new URI("ws://" + localConfig.host + ":946"); + yioRemoteDockActualStatus = YioRemoteDockHandleStatus.AUTHENTICATION_PROCESS; + } catch (URISyntaxException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "Initialize web socket failed: " + e.getMessage()); + } + try { + webSocketClient.start(); + webSocketClient.connect(yioremoteDockwebSocketClient, websocketAddress, + yioremoteDockwebSocketClientrequest); + } catch (Exception e) { + logger.debug("Connection error {}", e.getMessage()); + } + break; + case AUTHENTICATION_COMPLETE: + if (webSocketReconnectionPollingJob != null) { + if (!webSocketReconnectionPollingJob.isCancelled() && webSocketReconnectionPollingJob != null) { + webSocketReconnectionPollingJob.cancel(true); + } + webSocketReconnectionPollingJob = null; + } + break; + default: + break; + } + } + public void sendMessage(YioRemoteMessages messageType, String messagePayload) { switch (messageType) { case AUTHENTICATE_MESSAGE: diff --git a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/utils/Websocket.java b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/utils/Websocket.java index 13f40fdb8..053292b6b 100644 --- a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/utils/Websocket.java +++ b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/utils/Websocket.java @@ -17,6 +17,8 @@ import java.io.IOException; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.StatusCode; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; @@ -61,7 +63,25 @@ public class Websocket { public void onError(Throwable cause) { logger.warn("WebSocketError {}", cause.getMessage()); if (websocketHandler != null) { - websocketHandler.onError(); + websocketHandler.onError(cause); + } + } + + @OnWebSocketClose + public void onClose(int statusCode, String reason) { + if (statusCode != StatusCode.NORMAL) { + logger.debug("WebSocket Connection closed: {} - {}", statusCode, reason); + } + if (session != null) { + if (!session.isOpen()) { + if (session != null) { + session.close(); + } + } + session = null; + } + if (websocketHandler != null) { + websocketHandler.onClose(); } } @@ -74,4 +94,10 @@ public class Websocket { } } } + + public void closeWebsocketSession() { + if (session != null) { + session.close(); + } + } } diff --git a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/utils/WebsocketInterface.java b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/utils/WebsocketInterface.java index 8002992b9..9ec02a08d 100644 --- a/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/utils/WebsocketInterface.java +++ b/bundles/org.openhab.binding.yioremote/src/main/java/org/openhab/binding/yioremote/internal/utils/WebsocketInterface.java @@ -24,7 +24,9 @@ public interface WebsocketInterface { public void onConnect(boolean connected); + public void onClose(); + public void onMessage(String decodedmessage); - public void onError(); + public void onError(Throwable cause); }