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] Fix ONVIF alarms streams may stop and not restart on some cameras. #16777

Merged
merged 6 commits into from
May 20, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,11 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms
removeChannels.add(channel);
}
// start checking the input IO status
ipCameraHandler.lowPriorityRequests.set(0,
ipCameraHandler.lowPriorityRequests.add(0,
"/ISAPI/System/IO/inputs/" + ipCameraHandler.cameraConfig.getNvrChannel() + "/status");
} else {
// start checking the input IO status
ipCameraHandler.lowPriorityRequests.set(0,
ipCameraHandler.lowPriorityRequests.add(0,
"/ISAPI/System/IO/inputs/" + ipCameraHandler.cameraConfig.getNvrChannel() + "/status");
}
ipCameraHandler.removeChannels(removeChannels);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import org.openhab.binding.ipcamera.internal.MyNettyAuthHandler;
import org.openhab.binding.ipcamera.internal.ReolinkHandler;
import org.openhab.binding.ipcamera.internal.onvif.OnvifConnection;
import org.openhab.binding.ipcamera.internal.onvif.OnvifConnection.RequestType;
import org.openhab.binding.ipcamera.internal.servlet.CameraServlet;
import org.openhab.core.OpenHAB;
import org.openhab.core.library.types.DecimalType;
Expand Down Expand Up @@ -1556,19 +1557,13 @@ void pollCameraRunnable() {
// what needs to be done every poll//
switch (thing.getThingTypeUID().getId()) {
case GENERIC_THING:
if (!snapshotPolling) {
checkCameraConnection();
}
checkCameraConnection();
break;
case ONVIF_THING:
if (!snapshotPolling) {
checkCameraConnection();
}
onvifCamera.sendOnvifRequest(RequestType.Renew, onvifCamera.subscriptionXAddr);
break;
case INSTAR_THING:
if (!snapshotPolling) {
checkCameraConnection();
}
checkCameraConnection();
noMotionDetected(CHANNEL_MOTION_ALARM);
noMotionDetected(CHANNEL_PIR_ALARM);
noMotionDetected(CHANNEL_HUMAN_ALARM);
Expand All @@ -1588,19 +1583,14 @@ void pollCameraRunnable() {
sendHttpGET("/cgi-bin/eventManager.cgi?action=getEventIndexes&code=AudioMutation");
break;
case REOLINK_THING:
if (cameraConfig.getNvrChannel() > 0) {
sendHttpGET("/api.cgi?cmd=GetAiState&channel=" + cameraConfig.getNvrChannel() + "&user="
+ cameraConfig.getUser() + "&password=" + cameraConfig.getPassword());
sendHttpGET("/api.cgi?cmd=GetMdState&channel=" + cameraConfig.getNvrChannel() + "&user="
+ cameraConfig.getUser() + "&password=" + cameraConfig.getPassword());
} else if (!snapshotPolling) {
checkCameraConnection();
if (cameraConfig.getOnvifPort() == 0) {
sendHttpGET("/api.cgi?cmd=GetAiState&channel=" + cameraConfig.getNvrChannel() + reolinkAuth);
sendHttpGET("/api.cgi?cmd=GetMdState&channel=" + cameraConfig.getNvrChannel() + reolinkAuth);
} else {
onvifCamera.sendOnvifRequest(RequestType.Renew, onvifCamera.subscriptionXAddr);
}
break;
case DAHUA_THING:
if (!snapshotPolling) {
checkCameraConnection();
}
// Check for alarms, channel for NVRs appears not to work at filtering.
if (streamIsStopped("/cgi-bin/eventManager.cgi?action=attach&codes=[All]")) {
logger.info("The alarm stream was not running for camera {}, re-starting it now",
Expand All @@ -1609,9 +1599,6 @@ void pollCameraRunnable() {
}
break;
case DOORBIRD_THING:
if (!snapshotPolling) {
checkCameraConnection();
}
// Check for alarms, channel for NVRs appears not to work at filtering.
if (streamIsStopped("/bha-api/monitor.cgi?ring=doorbell,motionsensor")) {
logger.info("The alarm stream was not running for camera {}, re-starting it now",
Expand Down Expand Up @@ -1733,20 +1720,14 @@ public void initialize() {
"[{ \"cmd\":\"GetAbility\", \"param\":{ \"User\":{ \"userName\":\"admin\" }}}]");
}
if (snapshotUri.isEmpty()) {
if (cameraConfig.getNvrChannel() < 1) {
snapshotUri = "/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=openHAB" + reolinkAuth;
} else {
snapshotUri = "/cgi-bin/api.cgi?cmd=Snap&channel=" + (cameraConfig.getNvrChannel() - 1)
+ "&rs=openHAB" + reolinkAuth;
}
// ReolinkHandler will change the snapshotUri in the response to /api.cgi?cmd=Login
snapshotUri = "/cgi-bin/api.cgi?cmd=Snap&channel=" + cameraConfig.getNvrChannel() + "&rs=openHAB"
+ reolinkAuth;
}
// channel numbers for snapshots start at 0, while the rtsp start at 1
if (rtspUri.isEmpty()) {
if (cameraConfig.getNvrChannel() < 1) {
rtspUri = "rtsp://" + cameraConfig.getIp() + ":554/h264Preview_01_main";
} else {
rtspUri = "rtsp://" + cameraConfig.getIp() + ":554/h264Preview_0" + cameraConfig.getNvrChannel()
+ "_main";
}
rtspUri = "rtsp://" + cameraConfig.getIp() + ":554/h264Preview_0"
+ (cameraConfig.getNvrChannel() + 1) + "_main";
}
break;
}
Expand Down Expand Up @@ -1777,7 +1758,7 @@ private boolean supportsOnvifEvents() {
case ONVIF_THING:
return true;
case REOLINK_THING:
if (cameraConfig.getNvrChannel() < 1) {
if (cameraConfig.getOnvifPort() > 0) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
Expand All @@ -46,6 +47,11 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms
return;
}
try {
if (msg instanceof HttpResponse response) {
if (response.status().code() != 200) {
logger.trace("ONVIF replied with code {} message is {}", response.status().code(), msg);
}
}
if (msg instanceof HttpContent content) {
incomingMessage += content.content().toString(CharsetUtil.UTF_8);
}
Expand All @@ -65,11 +71,11 @@ public void userEventTriggered(@Nullable ChannelHandlerContext ctx, @Nullable Ob
}
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
logger.trace("IdleStateEvent received: {}", e.state());
logger.debug("IdleStateEvent received: {}", e.state());
onvifConnection.setIsConnected(false);
ctx.close();
} else {
logger.trace("Other ONVIF netty channel event occurred: {}", evt);
logger.debug("ONVIF netty channel event occurred: {}", evt);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public enum RequestType {
@SuppressWarnings("unused")
private String imagingXAddr = "http://" + ipAddress + "/onvif/device_service";
private String ptzXAddr = "http://" + ipAddress + "/onvif/ptz_service";
private String subscriptionXAddr = "http://" + ipAddress + "/onvif/device_service";
public String subscriptionXAddr = "http://" + ipAddress + "/onvif/device_service";
private boolean isConnected = false;
private int mediaProfileIndex = 0;
private String rtspUri = "";
Expand Down Expand Up @@ -269,7 +269,7 @@ private String getXml(RequestType requestType) {
+ mediaProfileTokens.get(mediaProfileIndex)
+ "</ProfileToken><Translation><Zoom x=\"-0.0240506344\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
case Renew:
return "<Renew xmlns=\"http://docs.oasis-open.org/wsn/b-2\"><TerminationTime>PT1M</TerminationTime></Renew>";
return "<Renew xmlns=\"http://docs.oasis-open.org/wsn/b-2\"><TerminationTime>PT10S</TerminationTime></Renew>";
case GetConfigurations:
return "<GetConfigurations xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"></GetConfigurations>";
case GetConfigurationOptions:
Expand Down Expand Up @@ -310,8 +310,8 @@ public void processReply(String message) {
logger.trace("ONVIF reply is: {}", message);
if (message.contains("PullMessagesResponse")) {
eventRecieved(message);
} else if (message.contains("RenewResponse")) {
sendOnvifRequest(RequestType.PullMessages, subscriptionXAddr);
} else if (message.contains("RenewResponse")) {
} else if (message.contains("GetSystemDateAndTimeResponse")) {// 1st to be sent.
setIsConnected(true);// Instar profile T only cameras need this
parseDateAndTime(message);
Expand Down Expand Up @@ -380,6 +380,8 @@ public void processReply(String message) {
ipCameraHandler.rtspUri = rtspUri;
}
}
} else {
logger.trace("Unhandled ONVIF reply is: {}", message);
}
}

Expand Down Expand Up @@ -568,7 +570,7 @@ public void sendOnvifRequest(RequestType requestType, String xAddr) {

@Override
public void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("idleStateHandler", new IdleStateHandler(20, 20, 20));
socketChannel.pipeline().addLast("idleStateHandler", new IdleStateHandler(0, 0, 18));
socketChannel.pipeline().addLast("HttpClientCodec", new HttpClientCodec());
socketChannel.pipeline().addLast("OnvifCodec", new OnvifCodec(getHandle()));
}
Expand All @@ -583,14 +585,14 @@ public void operationComplete(@Nullable ChannelFuture future) {
if (future == null) {
return;
}
if (future.isSuccess()) {
if (future.isDone() && future.isSuccess()) {
Channel ch = future.channel();
ch.writeAndFlush(request);
} else { // an error occurred
if (future.isDone() && !future.isCancelled()) {
Throwable cause = future.cause();
String msg = cause.getMessage();
logger.debug("connect failed - cause {}", cause.getMessage());
logger.debug("Connect failed - cause is: {}", cause.getMessage());
if (cause instanceof ConnectTimeoutException) {
usingEvents = false;// Prevent Unsubscribe from being sent
ipCameraHandler.cameraCommunicationError(
Expand All @@ -601,9 +603,8 @@ public void operationComplete(@Nullable ChannelFuture future) {
ipCameraHandler.cameraCommunicationError(
"Camera refused to connect when using ONVIF to port:" + port);
}
}
if (isConnected) {
disconnect();
} else {
ipCameraHandler.cameraCommunicationError("Camera failed to connect due to being cancelled");
}
}
}
Expand Down Expand Up @@ -652,7 +653,7 @@ public void gotoPreset(int index) {
public void eventRecieved(String eventMessage) {
String topic = Helper.fetchXML(eventMessage, "Topic", "tns1:");
if (topic.isEmpty()) {
sendOnvifRequest(RequestType.Renew, subscriptionXAddr);
logger.debug("No ONVIF Events occured in the last 8 seconds");
return;
}
String dataName = Helper.fetchXML(eventMessage, "tt:Data", "Name=\"");
Expand Down Expand Up @@ -782,7 +783,6 @@ public void eventRecieved(String eventMessage) {
default:
logger.debug("Please report this camera has an un-implemented ONVIF event. Topic: {}", topic);
}
sendOnvifRequest(RequestType.Renew, subscriptionXAddr);
}

public boolean supportsPTZ() {
Expand Down