Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ipcamera] Remove channels and stop checking IO status when camera does not support IO #16081

Merged
merged 1 commit into from Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -15,6 +15,8 @@
import static org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.*;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
Expand Down Expand Up @@ -53,6 +55,7 @@ public class HikvisionHandler extends ChannelDuplexHandler {
private IpCameraHandler ipCameraHandler;
private int nvrChannel;
private int lineCount, vmdCount, leftCount, takenCount, faceCount, pirCount, fieldCount;
private String requestUrl = "";

public HikvisionHandler(ThingHandler handler, int nvrChannel) {
ipCameraHandler = (IpCameraHandler) handler;
Expand Down Expand Up @@ -110,6 +113,10 @@ private void processEvent(String content) {
countDown();
}

public void setURL(String url) {
requestUrl = url;
}

// This handles the incoming http replies back from the camera.
@Override
public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception {
Expand All @@ -118,90 +125,145 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms
}
try {
String content = msg.toString();
logger.trace("HTTP Result back from camera is \t:{}:", content);
if (content.startsWith("--boundary")) {// Alarm checking goes in here//
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) {
case "MotionDetection version=":
ipCameraHandler.storeHttpReply(
"/ISAPI/System/Video/inputs/channels/" + nvrChannel + "01/motionDetection", content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.OFF);
logger.trace("HTTP Result from {} contains \t:{}:", requestUrl, content);
switch (requestUrl) {
case "/ISAPI/Event/notification/alertStream":
int startIndex = content.indexOf("<");// skip to start of XML content
if (startIndex != -1) {
String eventData = content.substring(startIndex, content.length());
processEvent(eventData);
}
return;
case "/ISAPI/System/IO/capabilities": // Used to check if the camera supports IO
List<org.openhab.core.thing.Channel> removeChannels = new ArrayList<>();
org.openhab.core.thing.Channel channel;
if (content.contains("<IOOutputPortNums>0<") || !content.contains("<IOOutputPortNums>")) {
logger.debug("Camera does not support IO outputs.");
channel = ipCameraHandler.getThing().getChannel(CHANNEL_ACTIVATE_ALARM_OUTPUT);
if (channel != null) {
removeChannels.add(channel);
}
break;
case "IOInputPort version=":
ipCameraHandler.storeHttpReply("/ISAPI/System/IO/inputs/" + nvrChannel, content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
channel = ipCameraHandler.getThing().getChannel(CHANNEL_ACTIVATE_ALARM_OUTPUT2);
if (channel != null) {
removeChannels.add(channel);
}
if (content.contains("<triggering>low</triggering>")) {
ipCameraHandler.setChannelState(CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
} else if (content.contains("<triggering>high</triggering>")) {
ipCameraHandler.setChannelState(CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT, OnOffType.ON);
} else if (content.contains("<IOOutputPortNums>1<")) {
channel = ipCameraHandler.getThing().getChannel(CHANNEL_ACTIVATE_ALARM_OUTPUT2);
if (channel != null) {
removeChannels.add(channel);
}
break;
case "LineDetection":
ipCameraHandler.storeHttpReply("/ISAPI/Smart/LineDetection/" + nvrChannel + "01", content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.OFF);
}
ipCameraHandler.lowPriorityRequests.clear(); // no longer need to check if the IO is supported.
if (content.contains("<IOInputPortNums>0<") || !content.contains("<IOInputPortNums>")) {
logger.debug("Camera does not support IO inputs.");
channel = ipCameraHandler.getThing().getChannel(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT);
if (channel != null) {
removeChannels.add(channel);
}
break;
case "TextOverlay version=":
ipCameraHandler.storeHttpReply(
"/ISAPI/System/Video/inputs/channels/" + nvrChannel + "/overlays/text/1", content);
String text = Helper.fetchXML(content, "<enabled>true</enabled>", "<displayText>");
ipCameraHandler.setChannelState(CHANNEL_TEXT_OVERLAY, StringType.valueOf(text));
break;
case "AudioDetection version=":
ipCameraHandler.storeHttpReply("/ISAPI/Smart/AudioDetection/channels/" + nvrChannel + "01",
content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.OFF);
channel = ipCameraHandler.getThing().getChannel(CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT);
if (channel != null) {
removeChannels.add(channel);
}
break;
case "IOPortStatus version=":
if (content.contains("<ioState>active</ioState>")) {
ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.ON);
} else if (content.contains("<ioState>inactive</ioState>")) {
ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
channel = ipCameraHandler.getThing().getChannel(CHANNEL_EXTERNAL_ALARM_INPUT);
if (channel != null) {
removeChannels.add(channel);
}
break;
case "FieldDetection version=":
ipCameraHandler.storeHttpReply("/ISAPI/Smart/FieldDetection/" + nvrChannel + "01", content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_FIELD_DETECTION_ALARM, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_FIELD_DETECTION_ALARM, OnOffType.OFF);
channel = ipCameraHandler.getThing().getChannel(CHANNEL_EXTERNAL_ALARM_INPUT2);
if (channel != null) {
removeChannels.add(channel);
}
break;
case "ResponseStatus version=":
////////////////// External Alarm Input ///////////////
if (content.contains(
"<requestURL>/ISAPI/System/IO/inputs/" + nvrChannel + "/status</requestURL>")) {
// Stops checking the external alarm if camera does not have feature.
if (content.contains("<statusString>Invalid Operation</statusString>")) {
ipCameraHandler.lowPriorityRequests.remove(0);
ipCameraHandler.logger.debug(
"Stopping checks for alarm inputs as camera appears to be missing this feature.");
}
} else if (content.contains("<IOInputPortNums>1<")) {
channel = ipCameraHandler.getThing().getChannel(CHANNEL_EXTERNAL_ALARM_INPUT2);
if (channel != null) {
removeChannels.add(channel);
}
break;
}
// start checking the input IO status
ipCameraHandler.lowPriorityRequests.set(0,
"/ISAPI/System/IO/inputs/" + ipCameraHandler.cameraConfig.getNvrChannel() + "/status");
} else {
// start checking the input IO status
ipCameraHandler.lowPriorityRequests.set(0,
"/ISAPI/System/IO/inputs/" + ipCameraHandler.cameraConfig.getNvrChannel() + "/status");
}
ipCameraHandler.removeChannels(removeChannels);
return;
}
String replyElement = Helper.fetchXML(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<");
switch (replyElement) {
case "MotionDetection version=":
ipCameraHandler.storeHttpReply(
"/ISAPI/System/Video/inputs/channels/" + nvrChannel + "01/motionDetection", content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.OFF);
}
break;
case "IOInputPort version=":
ipCameraHandler.storeHttpReply("/ISAPI/System/IO/inputs/" + nvrChannel, content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
}
if (content.contains("<triggering>low</triggering>")) {
ipCameraHandler.setChannelState(CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
} else if (content.contains("<triggering>high</triggering>")) {
ipCameraHandler.setChannelState(CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT, OnOffType.ON);
}
break;
case "LineDetection":
ipCameraHandler.storeHttpReply("/ISAPI/Smart/LineDetection/" + nvrChannel + "01", content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.OFF);
}
break;
case "TextOverlay version=":
ipCameraHandler.storeHttpReply(
"/ISAPI/System/Video/inputs/channels/" + nvrChannel + "/overlays/text/1", content);
String text = Helper.fetchXML(content, "<enabled>true</enabled>", "<displayText>");
ipCameraHandler.setChannelState(CHANNEL_TEXT_OVERLAY, StringType.valueOf(text));
break;
case "AudioDetection version=":
ipCameraHandler.storeHttpReply("/ISAPI/Smart/AudioDetection/channels/" + nvrChannel + "01",
content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.OFF);
}
break;
case "IOPortStatus version=":
if (content.contains("<ioState>active</ioState>")) {
ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.ON);
} else if (content.contains("<ioState>inactive</ioState>")) {
ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
}
break;
case "FieldDetection version=":
ipCameraHandler.storeHttpReply("/ISAPI/Smart/FieldDetection/" + nvrChannel + "01", content);
if (content.contains("<enabled>true</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_FIELD_DETECTION_ALARM, OnOffType.ON);
} else if (content.contains("<enabled>false</enabled>")) {
ipCameraHandler.setChannelState(CHANNEL_ENABLE_FIELD_DETECTION_ALARM, OnOffType.OFF);
}
break;
case "ResponseStatus version=":
////////////////// External Alarm Input ///////////////
if (content
.contains("<requestURL>/ISAPI/System/IO/inputs/" + nvrChannel + "/status</requestURL>")) {
// Stops checking the external alarm if camera does not have feature.
if (content.contains("<statusString>Invalid Operation</statusString>")) {
ipCameraHandler.lowPriorityRequests.remove(0);
ipCameraHandler.logger.debug(
"Stopping checks for alarm inputs as camera appears to be missing this feature.");
}
}
break;
}

} finally {
ReferenceCountUtil.release(msg);
}
Expand Down
Expand Up @@ -34,6 +34,7 @@ public class IpCameraBindingConstants {
public static final String COMMON_HANDLER = "commonHandler";
public static final String INSTAR_HANDLER = "instarHandler";
public static final String REOLINK_HANDLER = "reolinkHandler";
public static final String HIKVISION_HANDLER = "hikvisionHandler";

public enum FFmpegFormat {
HLS,
Expand Down
Expand Up @@ -583,8 +583,8 @@ public void initChannel(SocketChannel socketChannel) throws Exception {
new FoscamHandler(getHandle(), cameraConfig.getUser(), cameraConfig.getPassword()));
break;
case HIKVISION_THING:
socketChannel.pipeline()
.addLast(new HikvisionHandler(getHandle(), cameraConfig.getNvrChannel()));
socketChannel.pipeline().addLast(HIKVISION_HANDLER,
new HikvisionHandler(getHandle(), cameraConfig.getNvrChannel()));
break;
case INSTAR_THING:
socketChannel.pipeline().addLast(INSTAR_HANDLER, new InstarHandler(getHandle()));
Expand Down Expand Up @@ -654,6 +654,11 @@ public void operationComplete(@Nullable ChannelFuture future) {
AmcrestHandler amcrestHandler = (AmcrestHandler) ch.pipeline().get(AMCREST_HANDLER);
amcrestHandler.setURL(httpRequestURL);
break;
case HIKVISION_THING:
HikvisionHandler hikvisionHandler = (HikvisionHandler) ch.pipeline()
.get(HIKVISION_HANDLER);
hikvisionHandler.setURL(httpRequestURL);
break;
case INSTAR_THING:
InstarHandler instarHandler = (InstarHandler) ch.pipeline().get(INSTAR_HANDLER);
instarHandler.setURL(httpRequestURL);
Expand Down Expand Up @@ -1701,7 +1706,7 @@ public void initialize() {
snapshotUri = "/ISAPI/Streaming/channels/" + cameraConfig.getNvrChannel() + "01/picture";
}
if (lowPriorityRequests.isEmpty()) {
lowPriorityRequests.add("/ISAPI/System/IO/inputs/" + cameraConfig.getNvrChannel() + "/status");
lowPriorityRequests.add("/ISAPI/System/IO/capabilities");
}
break;
case INSTAR_THING:
Expand Down