From 84995bac835fafc76ae1d4696a857ba107a153ec Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Mon, 7 Dec 2020 16:56:25 +1100 Subject: [PATCH] [ipcamera] Improve ONVIF discovery and bug fixes. (#9199) * Fix Offline detection and Improve discovery. * Motion options bug fix. * Message content bug fix. * Fix all handlers to process all chunks as one. * Remove password from FFmpeg command log. Signed-off-by: Matthew Skinner --- .../ipcamera/internal/AmcrestHandler.java | 5 +- .../ipcamera/internal/DahuaHandler.java | 6 +- .../ipcamera/internal/DoorBirdHandler.java | 9 +-- .../binding/ipcamera/internal/Ffmpeg.java | 4 +- .../ipcamera/internal/FoscamHandler.java | 10 +-- .../ipcamera/internal/HikvisionHandler.java | 12 +-- .../ipcamera/internal/InstarHandler.java | 9 +-- .../internal/IpCameraDiscoveryService.java | 2 +- .../internal/handler/IpCameraHandler.java | 45 ++++++----- .../internal/onvif/OnvifDiscovery.java | 77 +++++++++---------- .../resources/OH-INF/thing/thing-types.xml | 2 +- 11 files changed, 82 insertions(+), 99 deletions(-) diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/AmcrestHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/AmcrestHandler.java index 8117d1e449..b48a34d749 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/AmcrestHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/AmcrestHandler.java @@ -61,10 +61,7 @@ public class AmcrestHandler extends ChannelDuplexHandler { } try { String content = msg.toString(); - - if (!content.isEmpty()) { - ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); - } + ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); if (content.contains("Error: No Events")) { if ("/cgi-bin/eventManager.cgi?action=getEventIndexes&code=VideoMotion".equals(requestUrl)) { ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM); diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java index 0a2c54c681..6cdc1aaaa8 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java @@ -55,11 +55,9 @@ public class DahuaHandler extends ChannelDuplexHandler { if (msg == null || ctx == null) { return; } - String content = msg.toString(); try { - if (!content.isEmpty()) { - ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); - } + String content = msg.toString(); + ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); // determine if the motion detection is turned on or off. if (content.contains("table.MotionDetect[0].Enable=true")) { ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.ON); diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java index 777f001240..499712704b 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DoorBirdHandler.java @@ -51,13 +51,9 @@ public class DoorBirdHandler extends ChannelDuplexHandler { if (msg == null || ctx == null) { return; } - String content = msg.toString(); try { - if (!content.isEmpty()) { - ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); - } else { - return; - } + String content = msg.toString(); + ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); if (content.contains("doorbell:H")) { ipCameraHandler.setChannelState(CHANNEL_DOORBELL, OnOffType.ON); } @@ -70,7 +66,6 @@ public class DoorBirdHandler extends ChannelDuplexHandler { if (content.contains("motionsensor:H")) { ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM); } - } finally { ReferenceCountUtil.release(msg); } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Ffmpeg.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Ffmpeg.java index 6e353933a1..d0741d303c 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Ffmpeg.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Ffmpeg.java @@ -53,10 +53,12 @@ public class Ffmpeg { private IpCameraFfmpegThread ipCameraFfmpegThread = new IpCameraFfmpegThread(); private int keepAlive = 8; private boolean running = false; + private String password; public Ffmpeg(IpCameraHandler handle, FFmpegFormat format, String ffmpegLocation, String inputArguments, String input, String outArguments, String output, String username, String password) { this.format = format; + this.password = password; ipCameraHandler = handle; String altInput = input; // Input can be snapshots not just rtsp or http @@ -169,7 +171,7 @@ public class Ffmpeg { public void startConverting() { if (!ipCameraFfmpegThread.isAlive()) { ipCameraFfmpegThread = new IpCameraFfmpegThread(); - logger.debug("Starting ffmpeg with this command now:{}", ffmpegCommand); + logger.debug("Starting ffmpeg with this command now:{}", ffmpegCommand.replaceAll(password, "********")); ipCameraFfmpegThread.start(); running = true; if (format.equals(FFmpegFormat.HLS)) { diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java index dd0dc57049..ad969440e4 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/FoscamHandler.java @@ -57,14 +57,9 @@ public class FoscamHandler extends ChannelDuplexHandler { if (msg == null || ctx == null) { return; } - String content = msg.toString(); try { - if (!content.isEmpty()) { - ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); - } else { - return; - } - + String content = msg.toString(); + ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); ////////////// Motion Alarm ////////////// if (content.contains("")) { if (content.contains("0")) { @@ -115,7 +110,6 @@ public class FoscamHandler extends ChannelDuplexHandler { ctx.close(); ipCameraHandler.logger.debug("End of FOSCAM handler reached, so closing the channel to the camera now"); } - } finally { ReferenceCountUtil.release(msg); } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java index 74580916f3..bb819c0ac3 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java @@ -67,15 +67,10 @@ public class HikvisionHandler extends ChannelDuplexHandler { if (msg == null || ctx == null) { return; } - String content = ""; - int debounce = 3; try { - content = msg.toString(); - if (content.isEmpty()) { - return; - } + int debounce = 3; + String content = msg.toString(); logger.trace("HTTP Result back from camera is \t:{}:", content); - if (content.contains("--boundary")) {// Alarm checking goes in here// if (content.contains("" + nvrChannel + " @@ -114,7 +109,8 @@ public class HikvisionHandler extends ChannelDuplexHandler { countDown(); countDown(); } - } else if (content.contains("0")) {// NVR uses channel 0 to say all channels + } else if (content.contains("0")) {// NVR uses channel 0 to say all + // channels if (content.contains("videoloss\r\ninactive")) { if (vmdCount > 1) { vmdCount = 1; diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/InstarHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/InstarHandler.java index 86a1d583b1..a6bec633a5 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/InstarHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/InstarHandler.java @@ -58,13 +58,10 @@ public class InstarHandler extends ChannelDuplexHandler { if (msg == null || ctx == null) { return; } - String content = ""; - String value1 = ""; try { - content = msg.toString(); - if (content.isEmpty()) { - return; - } + String value1 = ""; + String content = msg.toString(); + ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content); switch (requestUrl) { case "/param.cgi?cmd=getinfrared": if (content.contains("var infraredstat=\"auto")) { diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraDiscoveryService.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraDiscoveryService.java index 24cce552f1..46ba9ba62d 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraDiscoveryService.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraDiscoveryService.java @@ -68,7 +68,7 @@ public class IpCameraDiscoveryService extends AbstractDiscoveryService { removeOlderResults(getTimestampOfLastScan()); OnvifDiscovery onvifDiscovery = new OnvifDiscovery(this); try { - onvifDiscovery.discoverCameras(3702);// WS discovery + onvifDiscovery.discoverCameras(); } catch (UnknownHostException | InterruptedException e) { logger.warn( "IpCamera Discovery has an issue discovering the network settings to find cameras with. Try setting up the camera manually."); diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java index 4994cfb3e7..e58e2fb398 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java @@ -249,7 +249,7 @@ public class IpCameraHandler extends BaseThingHandler { } if (contentType.contains("multipart")) { closeConnection = false; - if (mjpegUri.contains(requestUrl)) { + if (mjpegUri.equals(requestUrl)) { if (msg instanceof HttpMessage) { // very start of stream only ReferenceCountUtil.retain(msg, 1); @@ -268,13 +268,13 @@ public class IpCameraHandler extends BaseThingHandler { } } if (msg instanceof HttpContent) { - if (mjpegUri.contains(requestUrl)) { + if (mjpegUri.equals(requestUrl)) { // multiple MJPEG stream packets come back as this. ReferenceCountUtil.retain(msg, 1); streamToGroup(msg, mjpegChannelGroup, true); } else { HttpContent content = (HttpContent) msg; - // Found some cameras uses Content-Type: image/jpg instead of image/jpeg + // Found some cameras use Content-Type: image/jpg instead of image/jpeg if (contentType.contains("image/jp")) { for (int i = 0; i < content.content().capacity(); i++) { incomingJpeg[bytesAlreadyRecieved++] = content.content().getByte(i); @@ -304,8 +304,8 @@ public class IpCameraHandler extends BaseThingHandler { super.channelRead(ctx, reply); } } - // HIKVISION alertStream never has a LastHttpContent as it always stays open// - if (contentType.contains("multipart")) { + // Alarm Streams never have a LastHttpContent as they always stay open// + else if (contentType.contains("multipart")) { if (bytesAlreadyRecieved != 0) { reply = incomingMessage; incomingMessage = ""; @@ -316,13 +316,14 @@ public class IpCameraHandler extends BaseThingHandler { } // Foscam needs this as will other cameras with chunks// if (isChunked && bytesAlreadyRecieved != 0) { + logger.debug("Reply is chunked."); reply = incomingMessage; super.channelRead(ctx, reply); } } } } else { // msg is not HttpContent - // Foscam and Amcrest cameras need this + // Foscam cameras need this if (!contentType.contains("image/jp") && bytesAlreadyRecieved != 0) { reply = incomingMessage; logger.debug("Packet back from camera is {}", incomingMessage); @@ -982,7 +983,6 @@ public class IpCameraHandler extends BaseThingHandler { } } String input = (cameraConfig.getAlarmInputUrl().isEmpty()) ? rtspUri : cameraConfig.getAlarmInputUrl(); - String outputOptions = "-f null -"; String filterOptions = ""; if (!audioAlarmEnabled) { filterOptions = "-an"; @@ -991,16 +991,22 @@ public class IpCameraHandler extends BaseThingHandler { } if (!motionAlarmEnabled && !ffmpegSnapshotGeneration) { filterOptions = filterOptions.concat(" -vn"); + } else if (motionAlarmEnabled && !cameraConfig.getMotionOptions().isEmpty()) { + String usersMotionOptions = cameraConfig.getMotionOptions(); + if (usersMotionOptions.startsWith("-")) { + // Need to put the users custom options first in the chain before the motion is detected + filterOptions += " " + usersMotionOptions + ",select='gte(scene," + motionThreshold + + ")',metadata=print"; + } else { + filterOptions = filterOptions + " " + usersMotionOptions + " -vf select='gte(scene," + + motionThreshold + ")',metadata=print"; + } } else if (motionAlarmEnabled) { filterOptions = filterOptions .concat(" -vf select='gte(scene," + motionThreshold + ")',metadata=print"); } - if (!cameraConfig.getUser().isEmpty()) { - filterOptions += " ";// add space as the Framework does not allow spaces at start of config. - } ffmpegRtspHelper = new Ffmpeg(this, format, cameraConfig.getFfmpegLocation(), inputOptions, input, - filterOptions + cameraConfig.getMotionOptions(), outputOptions, cameraConfig.getUser(), - cameraConfig.getPassword()); + filterOptions, "-f null -", cameraConfig.getUser(), cameraConfig.getPassword()); localAlarms = ffmpegRtspHelper; if (localAlarms != null) { localAlarms.startConverting(); @@ -1484,7 +1490,7 @@ public class IpCameraHandler extends BaseThingHandler { boolean streamIsStopped(String url) { ChannelTracking channelTracking = channelTrackingMap.get(url); if (channelTracking != null) { - if (channelTracking.getChannel().isOpen()) { + if (channelTracking.getChannel().isActive()) { return false; // stream is running. } } @@ -1534,20 +1540,21 @@ public class IpCameraHandler extends BaseThingHandler { } } - // runs every 8 seconds due to mjpeg streams not staying open unless they update this often. + /** + * {@link pollCameraRunnable} Polls every 8 seconds, to check camera is still ONLINE and keep mjpeg and alarm + * streams open and more. + * + */ void pollCameraRunnable() { // Snapshot should be first to keep consistent time between shots - if (!snapshotUri.isEmpty()) { - if (updateImageChannel) { - sendHttpGET(snapshotUri); - } - } if (streamingAutoFps) { updateAutoFps = true; if (!snapshotPolling && !ffmpegSnapshotGeneration) { // Dont need to poll if creating from RTSP stream with FFmpeg or we are polling at full rate already. sendHttpGET(snapshotUri); } + } else if (!snapshotUri.isEmpty() && !snapshotPolling) {// we need to check camera is still online. + sendHttpGET(snapshotUri); } // NOTE: Use lowPriorityRequests if get request is not needed every poll. if (!lowPriorityRequests.isEmpty()) { diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifDiscovery.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifDiscovery.java index ca2596030c..0e8f5600bd 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifDiscovery.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifDiscovery.java @@ -28,6 +28,7 @@ import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Enumeration; +import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -42,19 +43,21 @@ import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFactory; -import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOption; import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.InternetProtocolFamily; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.util.CharsetUtil; +import io.netty.util.concurrent.GlobalEventExecutor; /** - * The {@link OnvifDiscovery} is responsible for finding cameras that are Onvif using UDP multicast. + * The {@link OnvifDiscovery} is responsible for finding cameras that are ONVIF using UDP multicast. * * @author Matthew Skinner - Initial contribution */ @@ -69,7 +72,8 @@ public class OnvifDiscovery { this.ipCameraDiscoveryService = ipCameraDiscoveryService; } - public @Nullable NetworkInterface getLocalNIF() { + public @Nullable List getLocalNICs() { + List results = new ArrayList<>(2); try { for (Enumeration enumNetworks = NetworkInterface.getNetworkInterfaces(); enumNetworks .hasMoreElements();) { @@ -79,13 +83,13 @@ public class OnvifDiscovery { InetAddress inetAddress = enumIpAddr.nextElement(); if (!inetAddress.isLoopbackAddress() && inetAddress.getHostAddress().toString().length() < 18 && inetAddress.isSiteLocalAddress()) { - return networkInterface; + results.add(networkInterface); } } } } catch (SocketException ex) { } - return null; + return results; } void searchReply(String url, String xml) { @@ -180,23 +184,21 @@ public class OnvifDiscovery { return brand; } - public void discoverCameras(int port) throws UnknownHostException, InterruptedException { - String uuid = UUID.randomUUID().toString(); - String xml = ""; - - if (port == 3702) { - xml = "uuid:" - + uuid - + "urn:schemas-xmlsoap-org:ws:2005:04:discoveryhttp://schemas.xmlsoap.org/ws/2005/04/discovery/Probedp0:NetworkVideoTransmitter"; - } + private DatagramPacket wsDiscovery() throws UnknownHostException { + String xml = "uuid:" + + UUID.randomUUID() + + "urn:schemas-xmlsoap-org:ws:2005:04:discoveryhttp://schemas.xmlsoap.org/ws/2005/04/discovery/Probedp0:NetworkVideoTransmitter"; ByteBuf discoveryProbeMessage = Unpooled.copiedBuffer(xml, 0, xml.length(), StandardCharsets.UTF_8); - InetSocketAddress localNetworkAddress = new InetSocketAddress(0);// Listen for replies on all connections. - InetSocketAddress multiCastAddress = new InetSocketAddress(InetAddress.getByName("239.255.255.250"), port); - DatagramPacket datagramPacket = new DatagramPacket(discoveryProbeMessage, multiCastAddress, - localNetworkAddress); - NetworkInterface networkInterface = getLocalNIF(); - DatagramChannel datagramChannel; + return new DatagramPacket(discoveryProbeMessage, + new InetSocketAddress(InetAddress.getByName("239.255.255.250"), 3702), new InetSocketAddress(0)); + } + public void discoverCameras() throws UnknownHostException, InterruptedException { + List nics = getLocalNICs(); + if (nics == null || nics.isEmpty()) { + return; + } + NetworkInterface networkInterface = nics.get(0); Bootstrap bootstrap = new Bootstrap().group(new NioEventLoopGroup()) .channelFactory(new ChannelFactory() { @Override @@ -213,26 +215,21 @@ public class OnvifDiscovery { }).option(ChannelOption.SO_BROADCAST, true).option(ChannelOption.SO_REUSEADDR, true) .option(ChannelOption.IP_MULTICAST_LOOP_DISABLED, false).option(ChannelOption.SO_RCVBUF, 2048) .option(ChannelOption.IP_MULTICAST_TTL, 255).option(ChannelOption.IP_MULTICAST_IF, networkInterface); - - datagramChannel = (DatagramChannel) bootstrap.bind(localNetworkAddress).sync().channel(); - datagramChannel.joinGroup(multiCastAddress, networkInterface).sync(); - ChannelFuture chFuture; - if (port == 1900) { - String ssdp = "M-SEARCH * HTTP/1.1\n" + "HOST: 239.255.255.250:1900\n" + "MAN: \"ssdp:discover\"\n" - + "MX: 1\n" + "ST: urn:dial-multiscreen-org:service:dial:1\n" - + "USER-AGENT: Microsoft Edge/83.0.478.61 Windows\n" + "\n" + ""; - ByteBuf ssdpProbeMessage = Unpooled.copiedBuffer(ssdp, 0, ssdp.length(), StandardCharsets.UTF_8); - datagramPacket = new DatagramPacket(ssdpProbeMessage, multiCastAddress, localNetworkAddress); - chFuture = datagramChannel.writeAndFlush(datagramPacket); - } else { - chFuture = datagramChannel.writeAndFlush(datagramPacket); + ChannelGroup openChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + for (NetworkInterface nic : nics) { + DatagramChannel datagramChannel = (DatagramChannel) bootstrap.option(ChannelOption.IP_MULTICAST_IF, nic) + .bind(new InetSocketAddress(0)).sync().channel(); + datagramChannel + .joinGroup(new InetSocketAddress(InetAddress.getByName("239.255.255.250"), 3702), networkInterface) + .sync(); + openChannels.add(datagramChannel); + } + if (!openChannels.isEmpty()) { + openChannels.writeAndFlush(wsDiscovery()); + TimeUnit.SECONDS.sleep(6); + openChannels.close(); + processCameraReplys(); + bootstrap.config().group().shutdownGracefully(); } - chFuture.awaitUninterruptibly(2000); - chFuture = datagramChannel.closeFuture(); - TimeUnit.SECONDS.sleep(5); - datagramChannel.close(); - chFuture.awaitUninterruptibly(6000); - processCameraReplys(); - bootstrap.config().group().shutdownGracefully(); } } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml index 2081f08fae..1e354d4a30 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml @@ -2665,7 +2665,7 @@ Light - + Switch Turn the Privacy Mode on and off. -- 2.47.3