From 4a36985a17073904c45c3f6362eab1ae4a56e647 Mon Sep 17 00:00:00 2001 From: Matthew Skinner Date: Sun, 12 Dec 2021 19:08:53 +1100 Subject: [PATCH] [ipcamera] Add new channel lastEventData for detailed extra data on alarms (#11748) * Add new channel * Last Event Data channel finished. * Remove info logging. * Fix bugs * Fix ONVIF wont use different ports in xaddr paths. Signed-off-by: Matthew Skinner Signed-off-by: Michael Schmidt --- .../org.openhab.binding.ipcamera/README.md | 1 + .../ipcamera/internal/DahuaHandler.java | 10 +++++ .../binding/ipcamera/internal/Ffmpeg.java | 2 +- .../binding/ipcamera/internal/Helper.java | 6 +++ .../ipcamera/internal/HikvisionHandler.java | 7 ++- .../internal/IpCameraBindingConstants.java | 1 + .../internal/handler/IpCameraHandler.java | 2 +- .../internal/onvif/OnvifConnection.java | 43 +++++++++++-------- .../resources/OH-INF/thing/thing-types.xml | 9 ++++ 9 files changed, 60 insertions(+), 21 deletions(-) diff --git a/bundles/org.openhab.binding.ipcamera/README.md b/bundles/org.openhab.binding.ipcamera/README.md index ac7b6862eded4..037163166d4fe 100644 --- a/bundles/org.openhab.binding.ipcamera/README.md +++ b/bundles/org.openhab.binding.ipcamera/README.md @@ -238,6 +238,7 @@ The channels are kept consistent as much as possible from brand to brand to make | `itemLeft` | Switch (read only) | Will turn ON if an API camera detects an item has been left behind. | | `itemTaken` | Switch (read only) | Will turn ON if an API camera detects an item has been stolen. | | `lastMotionType` | String | Cameras with multiple alarm types will update this with which alarm last detected motion, i.e. a lineCrossing, faceDetection or item stolen alarm. You can also use this to create a timestamp of when the last motion was detected by creating a rule when this channel changes. | +| `lastEventData` | String | Detailed information about the last smart alarm that can contain information like which Line number was crossed and in which direction. The channel `lastMotionType` will hold the name of the alarm that this data belongs to. | | `lineCrossingAlarm` | Switch (read only) | Will turn on if the API camera detects motion has crossed a line. | | `mjpegUrl` | String | The URL for the ipcamera.mjpeg stream. | | `motionAlarm` | Switch (read only) | The status of the 'video motion' events in ONVIF and API cameras. Also see `cellMotionAlarm` as these can give different results. | 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 816233f2619a2..85489ab66a097 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 @@ -22,6 +22,7 @@ import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StringType; import org.openhab.core.thing.ChannelUID; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; @@ -63,6 +64,14 @@ private void processEvent(String content) { return; } String action = content.substring(startIndex, endIndex); + startIndex = content.indexOf(";data=", startIndex); + if (startIndex > 0) { + endIndex = content.lastIndexOf("}"); + if (endIndex > 0) { + String data = content.substring(startIndex + 6, endIndex + 1); + ipCameraHandler.setChannelState(CHANNEL_LAST_EVENT_DATA, new StringType(data)); + } + } switch (code) { case "VideoMotion": if ("Start".equals(action)) { @@ -106,6 +115,7 @@ private void processEvent(String content) { ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM); } break; + case "AudioAnomaly": case "AudioMutation": if ("Start".equals(action)) { ipCameraHandler.audioDetected(); 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 b940e76e8b4d7..36fdb5a094630 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 @@ -171,7 +171,7 @@ public void run() { } } } catch (IOException e) { - logger.warn("An error occured trying to process the messages from FFmpeg."); + logger.warn("An IO error occured trying to start FFmpeg:{}", e.getMessage()); } finally { switch (format) { case GIF: diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Helper.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Helper.java index 48cbab5ceeeb9..a66ee4d32a38c 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Helper.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Helper.java @@ -96,6 +96,12 @@ public static String fetchXML(String message, String sectionHeading, String key) if (sectionHeaderBeginning > 0) { result = result.substring(0, sectionHeaderBeginning); } + if (!key.endsWith(">")) { + startIndex = result.indexOf(">"); + if (startIndex != -1) { + return result.substring(startIndex + 1); + } + } return result; } 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 51ae91b6e16fa..7436fe31bd6f9 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 @@ -65,6 +65,7 @@ private void processEvent(String content) { if (content.contains("hannelID>" + nvrChannel) || content.contains("0")) { final int debounce = 3; String eventType = Helper.fetchXML(content, "", ""); + ipCameraHandler.setChannelState(CHANNEL_LAST_EVENT_DATA, new StringType(content)); switch (eventType) { case "videoloss": if (content.contains("inactive")) { @@ -120,7 +121,11 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms String content = msg.toString(); logger.trace("HTTP Result back from camera is \t:{}:", content); if (content.startsWith("--boundary")) {// Alarm checking goes in here// - processEvent(content); + int startIndex = content.indexOf("<");// skip to start of XML content + if (startIndex != -1) { + String eventData = content.substring(startIndex, content.length()); + processEvent(eventData); + } } else { String replyElement = Helper.fetchXML(content, "", "<"); switch (replyElement) { diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java index 49ed7f536f896..5fb04b07f9b0c 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java @@ -132,6 +132,7 @@ public static enum FFmpegFormat { public static final String CHANNEL_EXTERNAL_LIGHT = "externalLight"; public static final String CHANNEL_DOORBELL = "doorBell"; public static final String CHANNEL_LAST_MOTION_TYPE = "lastMotionType"; + public static final String CHANNEL_LAST_EVENT_DATA = "lastEventData"; public static final String CHANNEL_GOTO_PRESET = "gotoPreset"; public static final String CHANNEL_START_STREAM = "startStream"; public static final String CHANNEL_ENABLE_PRIVACY_MODE = "enablePrivacyMode"; 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 d0e90dd75906d..3fc0960b6a3a3 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 @@ -1366,7 +1366,7 @@ void pollingCameraConnection() { snapshotIsFfmpeg(); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Camera failed to report a valid Snaphot and/or RTSP URL. See readme on how to use the SNAPSHOT_URL_OVERRIDE feature."); + "Camera failed to report a valid Snaphot and/or RTSP URL. Check user/pass is correct, or use the advanced configs to manually provide a URL."); } } diff --git a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java index 95a0e109f2198..f5c2b1f4db12a 100644 --- a/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java +++ b/bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java @@ -119,13 +119,13 @@ public static enum RequestType { private String user = ""; private String password = ""; private int onvifPort = 80; - private String deviceXAddr = "/onvif/device_service"; - private String eventXAddr = "/onvif/device_service"; - private String mediaXAddr = "/onvif/device_service"; + private String deviceXAddr = "http://" + ipAddress + "/onvif/device_service"; + private String eventXAddr = "http://" + ipAddress + "/onvif/device_service"; + private String mediaXAddr = "http://" + ipAddress + "/onvif/device_service"; @SuppressWarnings("unused") - private String imagingXAddr = "/onvif/device_service"; - private String ptzXAddr = "/onvif/ptz_service"; - private String subscriptionXAddr = "/onvif/device_service"; + private String imagingXAddr = "http://" + ipAddress + "/onvif/device_service"; + private String ptzXAddr = "http://" + ipAddress + "/onvif/ptz_service"; + private String subscriptionXAddr = "http://" + ipAddress + "/onvif/device_service"; private boolean isConnected = false; private int mediaProfileIndex = 0; private String snapshotUri = ""; @@ -334,7 +334,7 @@ public void processReply(String message) { } else if (message.contains("GetEventPropertiesResponse")) { sendOnvifRequest(requestBuilder(RequestType.CreatePullPointSubscription, eventXAddr)); } else if (message.contains("CreatePullPointSubscriptionResponse")) { - subscriptionXAddr = removeIPfromUrl(Helper.fetchXML(message, "SubscriptionReference>", "Address>")); + subscriptionXAddr = Helper.fetchXML(message, "SubscriptionReference>", "Address>"); logger.debug("subscriptionXAddr={}", subscriptionXAddr); sendOnvifRequest(requestBuilder(RequestType.PullMessages, subscriptionXAddr)); } else if (message.contains("GetStatusResponse")) { @@ -376,7 +376,7 @@ HttpRequest requestBuilder(RequestType requestType, String xAddr) { String getXmlCache = getXml(requestType); if (requestType.equals(RequestType.CreatePullPointSubscription) || requestType.equals(RequestType.PullMessages) || requestType.equals(RequestType.Renew) || requestType.equals(RequestType.Unsubscribe)) { - headerTo = "http://" + ipAddress + xAddr + ""; + headerTo = "" + xAddr + ""; extraEnvelope = " xmlns:a=\"http://www.w3.org/2005/08/addressing\""; } String headers; @@ -396,16 +396,13 @@ HttpRequest requestBuilder(RequestType requestType, String xAddr) { } else {// GetSystemDateAndTime must not be password protected as per spec. headers = ""; } - FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, new HttpMethod("POST"), xAddr); + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, new HttpMethod("POST"), + removeIPfromUrl(xAddr)); String actionString = Helper.fetchXML(getXmlCache, requestType.toString(), "xmlns=\""); request.headers().add("Content-Type", "application/soap+xml; charset=utf-8; action=\"" + actionString + "/" + requestType + "\""); request.headers().add("Charset", "utf-8"); - if (onvifPort != 80) { - request.headers().set("Host", ipAddress + ":" + onvifPort); - } else { - request.headers().set("Host", ipAddress); - } + request.headers().set("Host", extractIPportFromUrl(xAddr)); request.headers().set("Connection", HttpHeaderValues.CLOSE); request.headers().set("Accept-Encoding", "gzip, deflate"); String fullXml = "" @@ -437,25 +434,35 @@ String removeIPfromUrl(String url) { return url.substring(index); } + String extractIPportFromUrl(String url) { + int startIndex = url.indexOf("//") + 2; + int endIndex = url.indexOf("/", startIndex);// skip past any :port to the slash / + if (startIndex != -1 && endIndex != -1) { + return url.substring(startIndex, endIndex); + } + logger.debug("We hit an issue extracting IP:PORT from url:{}", url); + return ""; + } + void parseXAddr(String message) { // Normally I would search '' instead but Foscam needed this work around. - String temp = removeIPfromUrl(Helper.fetchXML(message, ":{}", message); 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 c5b4dd7f40be3..d643a30a7ccba 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 @@ -880,6 +880,7 @@ + @@ -1710,6 +1711,7 @@ + @@ -2367,6 +2369,13 @@ + + String + + A string that contains detailed data on the last alarm that was triggered. + + + Switch