[ipcamera] Improve Hikvision alarm reliability (#10319)

Signed-off-by: Matthew Skinner <matt@pcmus.com>
This commit is contained in:
Matthew Skinner 2021-03-16 09:58:11 +11:00 committed by GitHub
parent 04da89cc4f
commit 16fba31556
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 87 deletions

View File

@ -60,6 +60,56 @@ public class HikvisionHandler extends ChannelDuplexHandler {
this.nvrChannel = nvrChannel;
}
private void processEvent(String content) {
// some cameras use <dynChannelID> or <channelID> and NVRs use channel 0 to say all channels
if (content.contains("hannelID>" + nvrChannel) || content.contains("<channelID>0</channelID>")) {
final int debounce = 3;
String eventType = Helper.fetchXML(content, "", "<eventType>");
switch (eventType) {
case "videoloss":
if (content.contains("<eventState>inactive</eventState>")) {
if (vmdCount > 1) {
vmdCount = 1;
}
countDown();
countDown();
}
break;
case "PIR":
ipCameraHandler.motionDetected(CHANNEL_PIR_ALARM);
pirCount = debounce;
break;
case "attendedBaggage":
ipCameraHandler.setChannelState(CHANNEL_ITEM_TAKEN, OnOffType.ON);
takenCount = debounce;
break;
case "unattendedBaggage":
ipCameraHandler.setChannelState(CHANNEL_ITEM_LEFT, OnOffType.ON);
leftCount = debounce;
break;
case "facedetection":
ipCameraHandler.setChannelState(CHANNEL_FACE_DETECTED, OnOffType.ON);
faceCount = debounce;
break;
case "VMD":
ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
vmdCount = debounce;
break;
case "fielddetection":
ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM);
fieldCount = debounce;
break;
case "linedetection":
ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM);
lineCount = debounce;
break;
default:
logger.debug("Unrecognised Hikvision eventType={}", eventType);
}
}
countDown();
}
// This handles the incoming http replies back from the camera.
@Override
public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception {
@ -67,59 +117,10 @@ public class HikvisionHandler extends ChannelDuplexHandler {
return;
}
try {
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("<EventNotificationAlert version=\"")) {
if (content.contains("hannelID>" + nvrChannel + "</")) {// some camera use c or <dynChannelID>
if (content.contains("<eventType>linedetection</eventType>")) {
ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM);
lineCount = debounce;
}
if (content.contains("<eventType>fielddetection</eventType>")) {
ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM);
fieldCount = debounce;
}
if (content.contains("<eventType>VMD</eventType>")) {
ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
vmdCount = debounce;
}
if (content.contains("<eventType>facedetection</eventType>")) {
ipCameraHandler.setChannelState(CHANNEL_FACE_DETECTED, OnOffType.ON);
faceCount = debounce;
}
if (content.contains("<eventType>unattendedBaggage</eventType>")) {
ipCameraHandler.setChannelState(CHANNEL_ITEM_LEFT, OnOffType.ON);
leftCount = debounce;
}
if (content.contains("<eventType>attendedBaggage</eventType>")) {
ipCameraHandler.setChannelState(CHANNEL_ITEM_TAKEN, OnOffType.ON);
takenCount = debounce;
}
if (content.contains("<eventType>PIR</eventType>")) {
ipCameraHandler.motionDetected(CHANNEL_PIR_ALARM);
pirCount = debounce;
}
if (content.contains("<eventType>videoloss</eventType>\r\n<eventState>inactive</eventState>")) {
if (vmdCount > 1) {
vmdCount = 1;
}
countDown();
countDown();
}
} else if (content.contains("<channelID>0</channelID>")) {// NVR uses channel 0 to say all
// channels
if (content.contains("<eventType>videoloss</eventType>\r\n<eventState>inactive</eventState>")) {
if (vmdCount > 1) {
vmdCount = 1;
}
countDown();
countDown();
}
}
countDown();
}
if (content.startsWith("--boundary")) {// Alarm checking goes in here//
processEvent(content);
} else {
String replyElement = Helper.fetchXML(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<");
switch (replyElement) {
@ -196,23 +197,7 @@ public class HikvisionHandler extends ChannelDuplexHandler {
}
break;
default:
if (content.contains("<EventNotificationAlert")) {
if (content.contains("hannelID>" + nvrChannel + "</")
|| content.contains("<channelID>0</channelID>")) {// some camera use c or
// <dynChannelID>
if (content.contains(
"<eventType>videoloss</eventType>\r\n<eventState>inactive</eventState>")) {
if (vmdCount > 1) {
vmdCount = 1;
}
countDown();
countDown();
}
countDown();
}
} else {
logger.debug("Unhandled reply-{}.", content);
}
logger.debug("Unhandled reply-{}.", content);
break;
}
}

View File

@ -206,6 +206,7 @@ public class IpCameraHandler extends BaseThingHandler {
private byte[] incomingJpeg = new byte[0];
private String incomingMessage = "";
private String contentType = "empty";
private String boundary = "";
private Object reply = new Object();
private String requestUrl = "";
private boolean closeConnection = true;
@ -255,6 +256,8 @@ public class IpCameraHandler extends BaseThingHandler {
firstStreamedMsg = msg;
streamToGroup(firstStreamedMsg, mjpegChannelGroup, true);
}
} else {
boundary = Helper.searchString(contentType, "boundary=");
}
} else if (contentType.contains("image/jp")) {
if (bytesToRecieve == 0) {
@ -305,11 +308,32 @@ public class IpCameraHandler extends BaseThingHandler {
}
// Alarm Streams never have a LastHttpContent as they always stay open//
else if (contentType.contains("multipart")) {
if (bytesAlreadyRecieved != 0) {
reply = incomingMessage;
incomingMessage = "";
bytesToRecieve = 0;
bytesAlreadyRecieved = 0;
int beginIndex, endIndex;
if (bytesToRecieve == 0) {
beginIndex = incomingMessage.indexOf("Content-Length:");
if (beginIndex != -1) {
endIndex = incomingMessage.indexOf("\r\n", beginIndex);
if (endIndex != -1) {
bytesToRecieve = Integer.parseInt(
incomingMessage.substring(beginIndex + 15, endIndex).strip());
}
}
}
// --boundary and headers are not included in the Content-Length value
if (bytesAlreadyRecieved > bytesToRecieve) {
// Check if message has a second --boundary
endIndex = incomingMessage.indexOf("--" + boundary, bytesToRecieve);
if (endIndex == -1) {
reply = incomingMessage;
incomingMessage = "";
bytesToRecieve = 0;
bytesAlreadyRecieved = 0;
} else {
reply = incomingMessage.substring(0, endIndex);
incomingMessage = incomingMessage.substring(endIndex, incomingMessage.length());
bytesToRecieve = 0;// Triggers search next time for Content-Length:
bytesAlreadyRecieved = incomingMessage.length() - endIndex;
}
super.channelRead(ctx, reply);
}
}
@ -1411,7 +1435,7 @@ public class IpCameraHandler extends BaseThingHandler {
updateState(channelToUpdate, valueOf);
}
void bringCameraOnline() {
private void bringCameraOnline() {
isOnline = true;
updateStatus(ThingStatus.ONLINE);
groupTracker.listOfOnlineCameraHandlers.add(this);
@ -1421,19 +1445,6 @@ public class IpCameraHandler extends BaseThingHandler {
localFuture.cancel(false);
}
switch (thing.getThingTypeUID().getId()) {
case HIKVISION_THING:
sendHttpGET("/ISAPI/Smart/AudioDetection/channels/" + cameraConfig.getNvrChannel() + "01");
sendHttpGET("/ISAPI/Smart/LineDetection/" + cameraConfig.getNvrChannel() + "01");
sendHttpGET("/ISAPI/Smart/FieldDetection/" + cameraConfig.getNvrChannel() + "01");
sendHttpGET(
"/ISAPI/System/Video/inputs/channels/" + cameraConfig.getNvrChannel() + "01/motionDetection");
sendHttpGET("/ISAPI/System/Video/inputs/channels/" + cameraConfig.getNvrChannel() + "/overlays/text/1");
sendHttpGET("/ISAPI/System/IO/inputs/" + cameraConfig.getNvrChannel());
sendHttpGET("/ISAPI/System/IO/inputs/" + cameraConfig.getNvrChannel());
break;
}
if (cameraConfig.getGifPreroll() > 0 || cameraConfig.getUpdateImageWhen().contains("1")) {
snapshotPolling = true;
snapshotJob = threadPool.scheduleWithFixedDelay(this::snapshotRunnable, 1000, cameraConfig.getPollTime(),