[ipcamera] Improve Hikvision alarm reliability (#10319)
Signed-off-by: Matthew Skinner <matt@pcmus.com>
This commit is contained in:
parent
04da89cc4f
commit
16fba31556
@ -60,6 +60,56 @@ public class HikvisionHandler extends ChannelDuplexHandler {
|
|||||||
this.nvrChannel = nvrChannel;
|
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.
|
// This handles the incoming http replies back from the camera.
|
||||||
@Override
|
@Override
|
||||||
public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception {
|
public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception {
|
||||||
@ -67,59 +117,10 @@ public class HikvisionHandler extends ChannelDuplexHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int debounce = 3;
|
|
||||||
String content = msg.toString();
|
String content = msg.toString();
|
||||||
logger.trace("HTTP Result back from camera is \t:{}:", content);
|
logger.trace("HTTP Result back from camera is \t:{}:", content);
|
||||||
if (content.contains("--boundary")) {// Alarm checking goes in here//
|
if (content.startsWith("--boundary")) {// Alarm checking goes in here//
|
||||||
if (content.contains("<EventNotificationAlert version=\"")) {
|
processEvent(content);
|
||||||
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();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
String replyElement = Helper.fetchXML(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<");
|
String replyElement = Helper.fetchXML(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<");
|
||||||
switch (replyElement) {
|
switch (replyElement) {
|
||||||
@ -196,23 +197,7 @@ public class HikvisionHandler extends ChannelDuplexHandler {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -206,6 +206,7 @@ public class IpCameraHandler extends BaseThingHandler {
|
|||||||
private byte[] incomingJpeg = new byte[0];
|
private byte[] incomingJpeg = new byte[0];
|
||||||
private String incomingMessage = "";
|
private String incomingMessage = "";
|
||||||
private String contentType = "empty";
|
private String contentType = "empty";
|
||||||
|
private String boundary = "";
|
||||||
private Object reply = new Object();
|
private Object reply = new Object();
|
||||||
private String requestUrl = "";
|
private String requestUrl = "";
|
||||||
private boolean closeConnection = true;
|
private boolean closeConnection = true;
|
||||||
@ -255,6 +256,8 @@ public class IpCameraHandler extends BaseThingHandler {
|
|||||||
firstStreamedMsg = msg;
|
firstStreamedMsg = msg;
|
||||||
streamToGroup(firstStreamedMsg, mjpegChannelGroup, true);
|
streamToGroup(firstStreamedMsg, mjpegChannelGroup, true);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
boundary = Helper.searchString(contentType, "boundary=");
|
||||||
}
|
}
|
||||||
} else if (contentType.contains("image/jp")) {
|
} else if (contentType.contains("image/jp")) {
|
||||||
if (bytesToRecieve == 0) {
|
if (bytesToRecieve == 0) {
|
||||||
@ -305,11 +308,32 @@ public class IpCameraHandler extends BaseThingHandler {
|
|||||||
}
|
}
|
||||||
// Alarm Streams never have a LastHttpContent as they always stay open//
|
// Alarm Streams never have a LastHttpContent as they always stay open//
|
||||||
else if (contentType.contains("multipart")) {
|
else if (contentType.contains("multipart")) {
|
||||||
if (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;
|
reply = incomingMessage;
|
||||||
incomingMessage = "";
|
incomingMessage = "";
|
||||||
bytesToRecieve = 0;
|
bytesToRecieve = 0;
|
||||||
bytesAlreadyRecieved = 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);
|
super.channelRead(ctx, reply);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1411,7 +1435,7 @@ public class IpCameraHandler extends BaseThingHandler {
|
|||||||
updateState(channelToUpdate, valueOf);
|
updateState(channelToUpdate, valueOf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bringCameraOnline() {
|
private void bringCameraOnline() {
|
||||||
isOnline = true;
|
isOnline = true;
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
groupTracker.listOfOnlineCameraHandlers.add(this);
|
groupTracker.listOfOnlineCameraHandlers.add(this);
|
||||||
@ -1421,19 +1445,6 @@ public class IpCameraHandler extends BaseThingHandler {
|
|||||||
localFuture.cancel(false);
|
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")) {
|
if (cameraConfig.getGifPreroll() > 0 || cameraConfig.getUpdateImageWhen().contains("1")) {
|
||||||
snapshotPolling = true;
|
snapshotPolling = true;
|
||||||
snapshotJob = threadPool.scheduleWithFixedDelay(this::snapshotRunnable, 1000, cameraConfig.getPollTime(),
|
snapshotJob = threadPool.scheduleWithFixedDelay(this::snapshotRunnable, 1000, cameraConfig.getPollTime(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user