]> git.basschouten.com Git - openhab-addons.git/commitdiff
[ipcamera] Add new channel lastEventData for detailed extra data on alarms (#11748)
authorMatthew Skinner <matt@pcmus.com>
Sun, 12 Dec 2021 08:08:53 +0000 (19:08 +1100)
committerGitHub <noreply@github.com>
Sun, 12 Dec 2021 08:08:53 +0000 (09:08 +0100)
* 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 <matt@pcmus.com>
bundles/org.openhab.binding.ipcamera/README.md
bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/DahuaHandler.java
bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Ffmpeg.java
bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/Helper.java
bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java
bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/IpCameraBindingConstants.java
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
bundles/org.openhab.binding.ipcamera/src/main/resources/OH-INF/thing/thing-types.xml

index ac7b6862eded49f0dc4b0c30b19c764d2256bb5e..037163166d4fe05559fc324fdd56d945efb25989 100644 (file)
@@ -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. |
index 816233f2619a20ef3087c3e2400d8af145964e52..85489ab66a097612553eda632b7f326a20037a55 100644 (file)
@@ -22,6 +22,7 @@ import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
 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 @@ public class DahuaHandler extends ChannelDuplexHandler {
             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 @@ public class DahuaHandler extends ChannelDuplexHandler {
                     ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM);
                 }
                 break;
+            case "AudioAnomaly":
             case "AudioMutation":
                 if ("Start".equals(action)) {
                     ipCameraHandler.audioDetected();
index b940e76e8b4d7f93c211777fdb82f0d80bedc0c3..36fdb5a094630b5580621611808cb53b4ad488fd 100644 (file)
@@ -171,7 +171,7 @@ public class Ffmpeg {
                     }
                 }
             } 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:
index 48cbab5ceeeb93da9652c0262528a8c77d4f9760..a66ee4d32a38c24a61aac02141f51916d77e6e2b 100644 (file)
@@ -96,6 +96,12 @@ public class Helper {
         if (sectionHeaderBeginning > 0) {
             result = result.substring(0, sectionHeaderBeginning);
         }
+        if (!key.endsWith(">")) {
+            startIndex = result.indexOf(">");
+            if (startIndex != -1) {
+                return result.substring(startIndex + 1);
+            }
+        }
         return result;
     }
 
index 51ae91b6e16faf23bb45175400c04d5cc51614e1..7436fe31bd6f9e8c2104c40bbd11b2ecc3a72f77 100644 (file)
@@ -65,6 +65,7 @@ public class HikvisionHandler extends ChannelDuplexHandler {
         if (content.contains("hannelID>" + nvrChannel) || content.contains("<channelID>0</channelID>")) {
             final int debounce = 3;
             String eventType = Helper.fetchXML(content, "", "<eventType>");
+            ipCameraHandler.setChannelState(CHANNEL_LAST_EVENT_DATA, new StringType(content));
             switch (eventType) {
                 case "videoloss":
                     if (content.contains("<eventState>inactive</eventState>")) {
@@ -120,7 +121,11 @@ public class HikvisionHandler extends ChannelDuplexHandler {
             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, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<");
                 switch (replyElement) {
index 49ed7f536f896eabf2d48374a7a600d9a2517c88..5fb04b07f9b0c0f9f78edf89b5786b3c56efe353 100644 (file)
@@ -132,6 +132,7 @@ public class IpCameraBindingConstants {
     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";
index d0e90dd75906d8da842255c55dc7f35b1225ea48..3fc0960b6a3a3483e6d7ecee1bee4a677e62cefa 100644 (file)
@@ -1366,7 +1366,7 @@ public class IpCameraHandler extends BaseThingHandler {
             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.");
         }
     }
 
index 95a0e109f2198a090d2666e3dd37bd134d4f5eb2..f5c2b1f4db12af5bc41b07bf5913567915b1090d 100644 (file)
@@ -119,13 +119,13 @@ public class OnvifConnection {
     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 class OnvifConnection {
         } 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 @@ public class OnvifConnection {
         String getXmlCache = getXml(requestType);
         if (requestType.equals(RequestType.CreatePullPointSubscription) || requestType.equals(RequestType.PullMessages)
                 || requestType.equals(RequestType.Renew) || requestType.equals(RequestType.Unsubscribe)) {
-            headerTo = "<a:To s:mustUnderstand=\"1\">http://" + ipAddress + xAddr + "</a:To>";
+            headerTo = "<a:To s:mustUnderstand=\"1\">" + xAddr + "</a:To>";
             extraEnvelope = " xmlns:a=\"http://www.w3.org/2005/08/addressing\"";
         }
         String headers;
@@ -396,16 +396,13 @@ public class OnvifConnection {
         } 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 = "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\"" + extraEnvelope + ">"
@@ -437,25 +434,35 @@ public class OnvifConnection {
         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 '<tt:XAddr>' instead but Foscam needed this work around.
-        String temp = removeIPfromUrl(Helper.fetchXML(message, "<tt:Device", "tt:XAddr"));
+        String temp = Helper.fetchXML(message, "<tt:Device", "tt:XAddr");
         if (!temp.isEmpty()) {
             deviceXAddr = temp;
             logger.debug("deviceXAddr:{}", deviceXAddr);
         }
-        temp = removeIPfromUrl(Helper.fetchXML(message, "<tt:Events", "tt:XAddr"));
+        temp = Helper.fetchXML(message, "<tt:Events", "tt:XAddr");
         if (!temp.isEmpty()) {
             subscriptionXAddr = eventXAddr = temp;
             logger.debug("eventsXAddr:{}", eventXAddr);
         }
-        temp = removeIPfromUrl(Helper.fetchXML(message, "<tt:Media", "tt:XAddr"));
+        temp = Helper.fetchXML(message, "<tt:Media", "tt:XAddr");
         if (!temp.isEmpty()) {
             mediaXAddr = temp;
             logger.debug("mediaXAddr:{}", mediaXAddr);
         }
 
-        ptzXAddr = removeIPfromUrl(Helper.fetchXML(message, "<tt:PTZ", "tt:XAddr"));
+        ptzXAddr = Helper.fetchXML(message, "<tt:PTZ", "tt:XAddr");
         if (ptzXAddr.isEmpty()) {
             ptzDevice = false;
             logger.trace("Camera must not support PTZ, it failed to give a <tt:PTZ><tt:XAddr>:{}", message);
index c5b4dd7f40be39ac22e6212a07e45a56dcd4eaf1..d643a30a7ccba312aa9ac7cf13ce9a7c1793af6d 100644 (file)
                        <channel id="mp4History" typeId="mp4History"/>
                        <channel id="mp4HistoryLength" typeId="mp4HistoryLength"/>
                        <channel id="lastMotionType" typeId="lastMotionType"/>
+                       <channel id="lastEventData" typeId="lastEventData"/>
                        <channel id="ffmpegMotionControl" typeId="ffmpegMotionControl"/>
                        <channel id="ffmpegMotionAlarm" typeId="ffmpegMotionAlarm"/>
                        <channel id="enableMotionAlarm" typeId="enableMotionAlarm"/>
                        <channel id="mp4History" typeId="mp4History"/>
                        <channel id="mp4HistoryLength" typeId="mp4HistoryLength"/>
                        <channel id="lastMotionType" typeId="lastMotionType"/>
+                       <channel id="lastEventData" typeId="lastEventData"/>
                        <channel id="ffmpegMotionControl" typeId="ffmpegMotionControl"/>
                        <channel id="ffmpegMotionAlarm" typeId="ffmpegMotionAlarm"/>
                        <channel id="enableMotionAlarm" typeId="enableMotionAlarm"/>
                <state readOnly="true"/>
        </channel-type>
 
+       <channel-type id="lastEventData" advanced="true">
+               <item-type>String</item-type>
+               <label>Last Event Data</label>
+               <description>A string that contains detailed data on the last alarm that was triggered.</description>
+               <state readOnly="true"/>
+       </channel-type>
+
        <channel-type id="motionAlarm">
                <item-type>Switch</item-type>
                <label>Motion Alarm</label>