]> git.basschouten.com Git - openhab-addons.git/commitdiff
Fix IndexOutOfBoundsException and remove Sleep. (#11089)
authorMatthew Skinner <matt@pcmus.com>
Wed, 11 Aug 2021 10:02:19 +0000 (20:02 +1000)
committerGitHub <noreply@github.com>
Wed, 11 Aug 2021 10:02:19 +0000 (12:02 +0200)
Signed-off-by: Matthew Skinner <matt@pcmus.com>
bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java
bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java

index 635b51f27ec9aaf9ad7ba6f3a5cd176c26e34f29..eacb18c1ecca9b2ab5bd6846eb27a30a00072460 100644 (file)
@@ -768,6 +768,10 @@ public class IpCameraHandler extends BaseThingHandler {
         }
     }
 
+    private void openMjpegStream() {
+        sendHttpGET(mjpegUri);
+    }
+
     // If start is true the CTX is added to the list to stream video to, false stops
     // the stream.
     public void setupMjpegStreaming(boolean start, ChannelHandlerContext ctx) {
@@ -777,13 +781,8 @@ public class IpCameraHandler extends BaseThingHandler {
                 if (mjpegUri.isEmpty() || "ffmpeg".equals(mjpegUri)) {
                     sendMjpegFirstPacket(ctx);
                     setupFfmpegFormat(FFmpegFormat.MJPEG);
-                } else {
-                    try {
-                        // fix Dahua reboots when refreshing a mjpeg stream.
-                        TimeUnit.MILLISECONDS.sleep(500);
-                    } catch (InterruptedException e) {
-                    }
-                    sendHttpGET(mjpegUri);
+                } else {// Delay fixes Dahua reboots when refreshing a mjpeg stream.
+                    threadPool.schedule(this::openMjpegStream, 500, TimeUnit.MILLISECONDS);
                 }
             } else if (ffmpegMjpeg != null) {// not first stream and we will use ffmpeg
                 sendMjpegFirstPacket(ctx);
@@ -1779,7 +1778,6 @@ public class IpCameraHandler extends BaseThingHandler {
     public void dispose() {
         isOnline = false;
         snapshotPolling = false;
-        onvifCamera.disconnect();
         Future<?> localFuture = pollCameraJob;
         if (localFuture != null) {
             localFuture.cancel(true);
@@ -1832,6 +1830,7 @@ public class IpCameraHandler extends BaseThingHandler {
             localFfmpeg.stopConverting();
         }
         channelTrackingMap.clear();
+        onvifCamera.disconnect();
     }
 
     public void setStreamServerHandler(StreamServerHandler streamServerHandler2) {
index eb86c7081e5ea0ab7488fa9be1ffd2edebdc22f9..6e03c9d4e4efb7efe5c18ccd4dda17c81719d0ea 100644 (file)
@@ -27,6 +27,8 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Random;
 import java.util.TimeZone;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -110,6 +112,7 @@ public class OnvifConnection {
     }
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
+    private ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);
     private @Nullable Bootstrap bootstrap;
     private EventLoopGroup mainEventLoopGroup = new NioEventLoopGroup();
     private String ipAddress = "";
@@ -162,129 +165,138 @@ public class OnvifConnection {
         }
     }
 
-    String getXml(RequestType requestType) {
-        switch (requestType) {
-            case AbsoluteMove:
-                return "<AbsoluteMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken><Position><PanTilt x=\""
-                        + currentPanCamValue + "\" y=\"" + currentTiltCamValue
-                        + "\" space=\"http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace\">\n"
-                        + "</PanTilt>\n" + "<Zoom x=\"" + currentZoomCamValue
-                        + "\" space=\"http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace\">\n"
-                        + "</Zoom>\n" + "</Position>\n"
-                        + "<Speed><PanTilt x=\"0.1\" y=\"0.1\" space=\"http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace\"></PanTilt><Zoom x=\"1.0\" space=\"http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace\"></Zoom>\n"
-                        + "</Speed></AbsoluteMove>";
-            case AddPTZConfiguration: // not tested to work yet
-                return "<AddPTZConfiguration xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken><ConfigurationToken>"
-                        + ptzConfigToken + "</ConfigurationToken></AddPTZConfiguration>";
-            case ContinuousMoveLeft:
-                return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Velocity><PanTilt x=\"-0.5\" y=\"0\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
-            case ContinuousMoveRight:
-                return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Velocity><PanTilt x=\"0.5\" y=\"0\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
-            case ContinuousMoveUp:
-                return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Velocity><PanTilt x=\"0\" y=\"-0.5\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
-            case ContinuousMoveDown:
-                return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Velocity><PanTilt x=\"0\" y=\"0.5\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
-            case Stop:
-                return "<Stop xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><PanTilt>true</PanTilt><Zoom>true</Zoom></Stop>";
-            case ContinuousMoveIn:
-                return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Velocity><Zoom x=\"0.5\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
-            case ContinuousMoveOut:
-                return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Velocity><Zoom x=\"-0.5\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
-            case CreatePullPointSubscription:
-                return "<CreatePullPointSubscription xmlns=\"http://www.onvif.org/ver10/events/wsdl\"><InitialTerminationTime>PT600S</InitialTerminationTime></CreatePullPointSubscription>";
-            case GetCapabilities:
-                return "<GetCapabilities xmlns=\"http://www.onvif.org/ver10/device/wsdl\"><Category>All</Category></GetCapabilities>";
-
-            case GetDeviceInformation:
-                return "<GetDeviceInformation xmlns=\"http://www.onvif.org/ver10/device/wsdl\"/>";
-            case GetProfiles:
-                return "<GetProfiles xmlns=\"http://www.onvif.org/ver10/media/wsdl\"/>";
-            case GetServiceCapabilities:
-                return "<GetServiceCapabilities xmlns=\"http://docs.oasis-open.org/wsn/b-2/\"></GetServiceCapabilities>";
-            case GetSnapshotUri:
-                return "<GetSnapshotUri xmlns=\"http://www.onvif.org/ver10/media/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken></GetSnapshotUri>";
-            case GetStreamUri:
-                return "<GetStreamUri xmlns=\"http://www.onvif.org/ver10/media/wsdl\"><StreamSetup><Stream xmlns=\"http://www.onvif.org/ver10/schema\">RTP-Unicast</Stream><Transport xmlns=\"http://www.onvif.org/ver10/schema\"><Protocol>RTSP</Protocol></Transport></StreamSetup><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken></GetStreamUri>";
-            case GetSystemDateAndTime:
-                return "<GetSystemDateAndTime xmlns=\"http://www.onvif.org/ver10/device/wsdl\"/>";
-            case Subscribe:
-                return "<Subscribe xmlns=\"http://docs.oasis-open.org/wsn/b-2/\"><ConsumerReference><Address>http://"
-                        + ipCameraHandler.hostIp + ":" + ipCameraHandler.cameraConfig.getServerPort()
-                        + "/OnvifEvent</Address></ConsumerReference></Subscribe>";
-            case Unsubscribe:
-                return "<Unsubscribe xmlns=\"http://docs.oasis-open.org/wsn/b-2/\"></Unsubscribe>";
-            case PullMessages:
-                return "<PullMessages xmlns=\"http://www.onvif.org/ver10/events/wsdl\"><Timeout>PT8S</Timeout><MessageLimit>1</MessageLimit></PullMessages>";
-            case GetEventProperties:
-                return "<GetEventProperties xmlns=\"http://www.onvif.org/ver10/events/wsdl\"/>";
-            case RelativeMoveLeft:
-                return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Translation><PanTilt x=\"0.05000000\" y=\"0\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
-            case RelativeMoveRight:
-                return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Translation><PanTilt x=\"-0.05000000\" y=\"0\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
-            case RelativeMoveUp:
-                return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Translation><PanTilt x=\"0\" y=\"0.100000000\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
-            case RelativeMoveDown:
-                return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Translation><PanTilt x=\"0\" y=\"-0.100000000\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
-            case RelativeMoveIn:
-                return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Translation><Zoom x=\"0.0240506344\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
-            case RelativeMoveOut:
-                return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex)
-                        + "</ProfileToken><Translation><Zoom x=\"-0.0240506344\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
-            case Renew:
-                return "<Renew xmlns=\"http://docs.oasis-open.org/wsn/b-2\"><TerminationTime>PT1M</TerminationTime></Renew>";
-            case GetConfigurations:
-                return "<GetConfigurations xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"></GetConfigurations>";
-            case GetConfigurationOptions:
-                return "<GetConfigurationOptions xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ConfigurationToken>"
-                        + ptzConfigToken + "</ConfigurationToken></GetConfigurationOptions>";
-            case GetConfiguration:
-                return "<GetConfiguration xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><PTZConfigurationToken>"
-                        + ptzConfigToken + "</PTZConfigurationToken></GetConfiguration>";
-            case SetConfiguration:// not tested to work yet
-                return "<SetConfiguration xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><PTZConfiguration><NodeToken>"
-                        + ptzNodeToken
-                        + "</NodeToken><DefaultAbsolutePantTiltPositionSpace>AbsolutePanTiltPositionSpace</DefaultAbsolutePantTiltPositionSpace><DefaultAbsoluteZoomPositionSpace>AbsoluteZoomPositionSpace</DefaultAbsoluteZoomPositionSpace></PTZConfiguration></SetConfiguration>";
-            case GetNodes:
-                return "<GetNodes xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"></GetNodes>";
-            case GetStatus:
-                return "<GetStatus xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken></GetStatus>";
-            case GotoPreset:
-                return "<GotoPreset xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken><PresetToken>"
-                        + presetTokens.get(presetTokenIndex) + "</PresetToken></GotoPreset>";
-            case GetPresets:
-                return "<GetPresets xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
-                        + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken></GetPresets>";
+    private String getXml(RequestType requestType) {
+        try {
+            switch (requestType) {
+                case AbsoluteMove:
+                    return "<AbsoluteMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken><Position><PanTilt x=\""
+                            + currentPanCamValue + "\" y=\"" + currentTiltCamValue
+                            + "\" space=\"http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace\">\n"
+                            + "</PanTilt>\n" + "<Zoom x=\"" + currentZoomCamValue
+                            + "\" space=\"http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace\">\n"
+                            + "</Zoom>\n" + "</Position>\n"
+                            + "<Speed><PanTilt x=\"0.1\" y=\"0.1\" space=\"http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace\"></PanTilt><Zoom x=\"1.0\" space=\"http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace\"></Zoom>\n"
+                            + "</Speed></AbsoluteMove>";
+                case AddPTZConfiguration: // not tested to work yet
+                    return "<AddPTZConfiguration xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken><ConfigurationToken>"
+                            + ptzConfigToken + "</ConfigurationToken></AddPTZConfiguration>";
+                case ContinuousMoveLeft:
+                    return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Velocity><PanTilt x=\"-0.5\" y=\"0\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
+                case ContinuousMoveRight:
+                    return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Velocity><PanTilt x=\"0.5\" y=\"0\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
+                case ContinuousMoveUp:
+                    return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Velocity><PanTilt x=\"0\" y=\"-0.5\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
+                case ContinuousMoveDown:
+                    return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Velocity><PanTilt x=\"0\" y=\"0.5\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
+                case Stop:
+                    return "<Stop xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><PanTilt>true</PanTilt><Zoom>true</Zoom></Stop>";
+                case ContinuousMoveIn:
+                    return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Velocity><Zoom x=\"0.5\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
+                case ContinuousMoveOut:
+                    return "<ContinuousMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Velocity><Zoom x=\"-0.5\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Velocity></ContinuousMove>";
+                case CreatePullPointSubscription:
+                    return "<CreatePullPointSubscription xmlns=\"http://www.onvif.org/ver10/events/wsdl\"><InitialTerminationTime>PT600S</InitialTerminationTime></CreatePullPointSubscription>";
+                case GetCapabilities:
+                    return "<GetCapabilities xmlns=\"http://www.onvif.org/ver10/device/wsdl\"><Category>All</Category></GetCapabilities>";
+
+                case GetDeviceInformation:
+                    return "<GetDeviceInformation xmlns=\"http://www.onvif.org/ver10/device/wsdl\"/>";
+                case GetProfiles:
+                    return "<GetProfiles xmlns=\"http://www.onvif.org/ver10/media/wsdl\"/>";
+                case GetServiceCapabilities:
+                    return "<GetServiceCapabilities xmlns=\"http://docs.oasis-open.org/wsn/b-2/\"></GetServiceCapabilities>";
+                case GetSnapshotUri:
+                    return "<GetSnapshotUri xmlns=\"http://www.onvif.org/ver10/media/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken></GetSnapshotUri>";
+                case GetStreamUri:
+                    return "<GetStreamUri xmlns=\"http://www.onvif.org/ver10/media/wsdl\"><StreamSetup><Stream xmlns=\"http://www.onvif.org/ver10/schema\">RTP-Unicast</Stream><Transport xmlns=\"http://www.onvif.org/ver10/schema\"><Protocol>RTSP</Protocol></Transport></StreamSetup><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken></GetStreamUri>";
+                case GetSystemDateAndTime:
+                    return "<GetSystemDateAndTime xmlns=\"http://www.onvif.org/ver10/device/wsdl\"/>";
+                case Subscribe:
+                    return "<Subscribe xmlns=\"http://docs.oasis-open.org/wsn/b-2/\"><ConsumerReference><Address>http://"
+                            + ipCameraHandler.hostIp + ":" + ipCameraHandler.cameraConfig.getServerPort()
+                            + "/OnvifEvent</Address></ConsumerReference></Subscribe>";
+                case Unsubscribe:
+                    return "<Unsubscribe xmlns=\"http://docs.oasis-open.org/wsn/b-2/\"></Unsubscribe>";
+                case PullMessages:
+                    return "<PullMessages xmlns=\"http://www.onvif.org/ver10/events/wsdl\"><Timeout>PT8S</Timeout><MessageLimit>1</MessageLimit></PullMessages>";
+                case GetEventProperties:
+                    return "<GetEventProperties xmlns=\"http://www.onvif.org/ver10/events/wsdl\"/>";
+                case RelativeMoveLeft:
+                    return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Translation><PanTilt x=\"0.05000000\" y=\"0\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
+                case RelativeMoveRight:
+                    return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Translation><PanTilt x=\"-0.05000000\" y=\"0\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
+                case RelativeMoveUp:
+                    return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Translation><PanTilt x=\"0\" y=\"0.100000000\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
+                case RelativeMoveDown:
+                    return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Translation><PanTilt x=\"0\" y=\"-0.100000000\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
+                case RelativeMoveIn:
+                    return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Translation><Zoom x=\"0.0240506344\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
+                case RelativeMoveOut:
+                    return "<RelativeMove xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex)
+                            + "</ProfileToken><Translation><Zoom x=\"-0.0240506344\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
+                case Renew:
+                    return "<Renew xmlns=\"http://docs.oasis-open.org/wsn/b-2\"><TerminationTime>PT1M</TerminationTime></Renew>";
+                case GetConfigurations:
+                    return "<GetConfigurations xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"></GetConfigurations>";
+                case GetConfigurationOptions:
+                    return "<GetConfigurationOptions xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ConfigurationToken>"
+                            + ptzConfigToken + "</ConfigurationToken></GetConfigurationOptions>";
+                case GetConfiguration:
+                    return "<GetConfiguration xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><PTZConfigurationToken>"
+                            + ptzConfigToken + "</PTZConfigurationToken></GetConfiguration>";
+                case SetConfiguration:// not tested to work yet
+                    return "<SetConfiguration xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><PTZConfiguration><NodeToken>"
+                            + ptzNodeToken
+                            + "</NodeToken><DefaultAbsolutePantTiltPositionSpace>AbsolutePanTiltPositionSpace</DefaultAbsolutePantTiltPositionSpace><DefaultAbsoluteZoomPositionSpace>AbsoluteZoomPositionSpace</DefaultAbsoluteZoomPositionSpace></PTZConfiguration></SetConfiguration>";
+                case GetNodes:
+                    return "<GetNodes xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"></GetNodes>";
+                case GetStatus:
+                    return "<GetStatus xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken></GetStatus>";
+                case GotoPreset:
+                    return "<GotoPreset xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken><PresetToken>"
+                            + presetTokens.get(presetTokenIndex) + "</PresetToken></GotoPreset>";
+                case GetPresets:
+                    return "<GetPresets xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"><ProfileToken>"
+                            + mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken></GetPresets>";
+            }
+        } catch (IndexOutOfBoundsException e) {
+            if (!isConnected) {
+                logger.debug("IndexOutOfBoundsException occured, camera is not connected via ONVIF: {}",
+                        e.getMessage());
+            } else {
+                logger.debug("IndexOutOfBoundsException occured, {}", e.getMessage());
+            }
         }
         return "notfound";
     }
@@ -805,6 +817,10 @@ public class OnvifConnection {
     }
 
     public void sendPTZRequest(RequestType requestType) {
+        if (!isConnected) {
+            logger.debug("ONVIF was not connected when a PTZ request was made, connecting now");
+            connect(usingEvents);
+        }
         sendOnvifRequest(requestBuilder(requestType, ptzXAddr));
     }
 
@@ -823,26 +839,28 @@ public class OnvifConnection {
         return isConnected;
     }
 
-    public void disconnect() {
-        if (usingEvents && isConnected) {
-            sendOnvifRequest(requestBuilder(RequestType.Unsubscribe, subscriptionXAddr));
-            try {
-                Thread.sleep(500);
-            } catch (InterruptedException e) {
-            }
-        }
+    private void cleanup() {
+        mainEventLoopGroup.shutdownGracefully();
         isConnected = false;
-        presetTokens.clear();
-        mediaProfileTokens.clear();
         if (!mainEventLoopGroup.isShutdown()) {
             try {
                 mainEventLoopGroup.awaitTermination(3, TimeUnit.SECONDS);
             } catch (InterruptedException e) {
-                logger.info("Onvif was not shutdown correctly due to being interrupted");
+                logger.warn("ONVIF was not cleanly shutdown, due to being interrupted");
             } finally {
+                logger.debug("Eventloop is shutdown:{}", mainEventLoopGroup.isShutdown());
                 mainEventLoopGroup = new NioEventLoopGroup();
                 bootstrap = null;
             }
         }
+        threadPool.shutdown();
+    }
+
+    public void disconnect() {
+        if (usingEvents && isConnected && !mainEventLoopGroup.isShuttingDown()) {
+            sendOnvifRequest(requestBuilder(RequestType.Unsubscribe, subscriptionXAddr));
+        }
+        // Some cameras may continue to send event callbacks even when they cant reach a server.
+        threadPool.schedule(this::cleanup, 500, TimeUnit.MILLISECONDS);
     }
 }