Skip to content

Commit f7ebff9

Browse files
authored
[ipcamera] Fix ONVIF alarms streams may stop and not restart on some cameras. (openhab#16777)
* Fault find stream Signed-off-by: Matthew Skinner <[email protected]>
1 parent 659423f commit f7ebff9

File tree

4 files changed

+37
-50
lines changed

4 files changed

+37
-50
lines changed

bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/HikvisionHandler.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms
178178
removeChannels.add(channel);
179179
}
180180
// start checking the input IO status
181-
ipCameraHandler.lowPriorityRequests.set(0,
181+
ipCameraHandler.lowPriorityRequests.add(0,
182182
"/ISAPI/System/IO/inputs/" + ipCameraHandler.cameraConfig.getNvrChannel() + "/status");
183183
} else {
184184
// start checking the input IO status
185-
ipCameraHandler.lowPriorityRequests.set(0,
185+
ipCameraHandler.lowPriorityRequests.add(0,
186186
"/ISAPI/System/IO/inputs/" + ipCameraHandler.cameraConfig.getNvrChannel() + "/status");
187187
}
188188
ipCameraHandler.removeChannels(removeChannels);

bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/handler/IpCameraHandler.java

+16-35
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.openhab.binding.ipcamera.internal.MyNettyAuthHandler;
6262
import org.openhab.binding.ipcamera.internal.ReolinkHandler;
6363
import org.openhab.binding.ipcamera.internal.onvif.OnvifConnection;
64+
import org.openhab.binding.ipcamera.internal.onvif.OnvifConnection.RequestType;
6465
import org.openhab.binding.ipcamera.internal.servlet.CameraServlet;
6566
import org.openhab.core.OpenHAB;
6667
import org.openhab.core.library.types.DecimalType;
@@ -1556,19 +1557,13 @@ void pollCameraRunnable() {
15561557
// what needs to be done every poll//
15571558
switch (thing.getThingTypeUID().getId()) {
15581559
case GENERIC_THING:
1559-
if (!snapshotPolling) {
1560-
checkCameraConnection();
1561-
}
1560+
checkCameraConnection();
15621561
break;
15631562
case ONVIF_THING:
1564-
if (!snapshotPolling) {
1565-
checkCameraConnection();
1566-
}
1563+
onvifCamera.sendOnvifRequest(RequestType.Renew, onvifCamera.subscriptionXAddr);
15671564
break;
15681565
case INSTAR_THING:
1569-
if (!snapshotPolling) {
1570-
checkCameraConnection();
1571-
}
1566+
checkCameraConnection();
15721567
noMotionDetected(CHANNEL_MOTION_ALARM);
15731568
noMotionDetected(CHANNEL_PIR_ALARM);
15741569
noMotionDetected(CHANNEL_HUMAN_ALARM);
@@ -1588,19 +1583,14 @@ void pollCameraRunnable() {
15881583
sendHttpGET("/cgi-bin/eventManager.cgi?action=getEventIndexes&code=AudioMutation");
15891584
break;
15901585
case REOLINK_THING:
1591-
if (cameraConfig.getNvrChannel() > 0) {
1592-
sendHttpGET("/api.cgi?cmd=GetAiState&channel=" + cameraConfig.getNvrChannel() + "&user="
1593-
+ cameraConfig.getUser() + "&password=" + cameraConfig.getPassword());
1594-
sendHttpGET("/api.cgi?cmd=GetMdState&channel=" + cameraConfig.getNvrChannel() + "&user="
1595-
+ cameraConfig.getUser() + "&password=" + cameraConfig.getPassword());
1596-
} else if (!snapshotPolling) {
1597-
checkCameraConnection();
1586+
if (cameraConfig.getOnvifPort() == 0) {
1587+
sendHttpGET("/api.cgi?cmd=GetAiState&channel=" + cameraConfig.getNvrChannel() + reolinkAuth);
1588+
sendHttpGET("/api.cgi?cmd=GetMdState&channel=" + cameraConfig.getNvrChannel() + reolinkAuth);
1589+
} else {
1590+
onvifCamera.sendOnvifRequest(RequestType.Renew, onvifCamera.subscriptionXAddr);
15981591
}
15991592
break;
16001593
case DAHUA_THING:
1601-
if (!snapshotPolling) {
1602-
checkCameraConnection();
1603-
}
16041594
// Check for alarms, channel for NVRs appears not to work at filtering.
16051595
if (streamIsStopped("/cgi-bin/eventManager.cgi?action=attach&codes=[All]")) {
16061596
logger.info("The alarm stream was not running for camera {}, re-starting it now",
@@ -1609,9 +1599,6 @@ void pollCameraRunnable() {
16091599
}
16101600
break;
16111601
case DOORBIRD_THING:
1612-
if (!snapshotPolling) {
1613-
checkCameraConnection();
1614-
}
16151602
// Check for alarms, channel for NVRs appears not to work at filtering.
16161603
if (streamIsStopped("/bha-api/monitor.cgi?ring=doorbell,motionsensor")) {
16171604
logger.info("The alarm stream was not running for camera {}, re-starting it now",
@@ -1733,20 +1720,14 @@ public void initialize() {
17331720
"[{ \"cmd\":\"GetAbility\", \"param\":{ \"User\":{ \"userName\":\"admin\" }}}]");
17341721
}
17351722
if (snapshotUri.isEmpty()) {
1736-
if (cameraConfig.getNvrChannel() < 1) {
1737-
snapshotUri = "/cgi-bin/api.cgi?cmd=Snap&channel=0&rs=openHAB" + reolinkAuth;
1738-
} else {
1739-
snapshotUri = "/cgi-bin/api.cgi?cmd=Snap&channel=" + (cameraConfig.getNvrChannel() - 1)
1740-
+ "&rs=openHAB" + reolinkAuth;
1741-
}
1723+
// ReolinkHandler will change the snapshotUri in the response to /api.cgi?cmd=Login
1724+
snapshotUri = "/cgi-bin/api.cgi?cmd=Snap&channel=" + cameraConfig.getNvrChannel() + "&rs=openHAB"
1725+
+ reolinkAuth;
17421726
}
1727+
// channel numbers for snapshots start at 0, while the rtsp start at 1
17431728
if (rtspUri.isEmpty()) {
1744-
if (cameraConfig.getNvrChannel() < 1) {
1745-
rtspUri = "rtsp://" + cameraConfig.getIp() + ":554/h264Preview_01_main";
1746-
} else {
1747-
rtspUri = "rtsp://" + cameraConfig.getIp() + ":554/h264Preview_0" + cameraConfig.getNvrChannel()
1748-
+ "_main";
1749-
}
1729+
rtspUri = "rtsp://" + cameraConfig.getIp() + ":554/h264Preview_0"
1730+
+ (cameraConfig.getNvrChannel() + 1) + "_main";
17501731
}
17511732
break;
17521733
}
@@ -1777,7 +1758,7 @@ private boolean supportsOnvifEvents() {
17771758
case ONVIF_THING:
17781759
return true;
17791760
case REOLINK_THING:
1780-
if (cameraConfig.getNvrChannel() < 1) {
1761+
if (cameraConfig.getOnvifPort() > 0) {
17811762
return true;
17821763
}
17831764
}

bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifCodec.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.netty.channel.ChannelDuplexHandler;
2121
import io.netty.channel.ChannelHandlerContext;
2222
import io.netty.handler.codec.http.HttpContent;
23+
import io.netty.handler.codec.http.HttpResponse;
2324
import io.netty.handler.codec.http.LastHttpContent;
2425
import io.netty.handler.timeout.IdleStateEvent;
2526
import io.netty.util.CharsetUtil;
@@ -46,6 +47,11 @@ public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object ms
4647
return;
4748
}
4849
try {
50+
if (msg instanceof HttpResponse response) {
51+
if (response.status().code() != 200) {
52+
logger.trace("ONVIF replied with code {} message is {}", response.status().code(), msg);
53+
}
54+
}
4955
if (msg instanceof HttpContent content) {
5056
incomingMessage += content.content().toString(CharsetUtil.UTF_8);
5157
}
@@ -65,11 +71,11 @@ public void userEventTriggered(@Nullable ChannelHandlerContext ctx, @Nullable Ob
6571
}
6672
if (evt instanceof IdleStateEvent) {
6773
IdleStateEvent e = (IdleStateEvent) evt;
68-
logger.trace("IdleStateEvent received: {}", e.state());
74+
logger.debug("IdleStateEvent received: {}", e.state());
6975
onvifConnection.setIsConnected(false);
7076
ctx.close();
7177
} else {
72-
logger.trace("Other ONVIF netty channel event occurred: {}", evt);
78+
logger.debug("ONVIF netty channel event occurred: {}", evt);
7379
}
7480
}
7581

bundles/org.openhab.binding.ipcamera/src/main/java/org/openhab/binding/ipcamera/internal/onvif/OnvifConnection.java

+11-11
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public enum RequestType {
128128
@SuppressWarnings("unused")
129129
private String imagingXAddr = "http://" + ipAddress + "/onvif/device_service";
130130
private String ptzXAddr = "http://" + ipAddress + "/onvif/ptz_service";
131-
private String subscriptionXAddr = "http://" + ipAddress + "/onvif/device_service";
131+
public String subscriptionXAddr = "http://" + ipAddress + "/onvif/device_service";
132132
private boolean isConnected = false;
133133
private int mediaProfileIndex = 0;
134134
private String rtspUri = "";
@@ -269,7 +269,7 @@ private String getXml(RequestType requestType) {
269269
+ mediaProfileTokens.get(mediaProfileIndex)
270270
+ "</ProfileToken><Translation><Zoom x=\"-0.0240506344\" xmlns=\"http://www.onvif.org/ver10/schema\"/></Translation></RelativeMove>";
271271
case Renew:
272-
return "<Renew xmlns=\"http://docs.oasis-open.org/wsn/b-2\"><TerminationTime>PT1M</TerminationTime></Renew>";
272+
return "<Renew xmlns=\"http://docs.oasis-open.org/wsn/b-2\"><TerminationTime>PT10S</TerminationTime></Renew>";
273273
case GetConfigurations:
274274
return "<GetConfigurations xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"></GetConfigurations>";
275275
case GetConfigurationOptions:
@@ -310,8 +310,8 @@ public void processReply(String message) {
310310
logger.trace("ONVIF reply is: {}", message);
311311
if (message.contains("PullMessagesResponse")) {
312312
eventRecieved(message);
313-
} else if (message.contains("RenewResponse")) {
314313
sendOnvifRequest(RequestType.PullMessages, subscriptionXAddr);
314+
} else if (message.contains("RenewResponse")) {
315315
} else if (message.contains("GetSystemDateAndTimeResponse")) {// 1st to be sent.
316316
setIsConnected(true);// Instar profile T only cameras need this
317317
parseDateAndTime(message);
@@ -380,6 +380,8 @@ public void processReply(String message) {
380380
ipCameraHandler.rtspUri = rtspUri;
381381
}
382382
}
383+
} else {
384+
logger.trace("Unhandled ONVIF reply is: {}", message);
383385
}
384386
}
385387

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

569571
@Override
570572
public void initChannel(SocketChannel socketChannel) throws Exception {
571-
socketChannel.pipeline().addLast("idleStateHandler", new IdleStateHandler(20, 20, 20));
573+
socketChannel.pipeline().addLast("idleStateHandler", new IdleStateHandler(0, 0, 18));
572574
socketChannel.pipeline().addLast("HttpClientCodec", new HttpClientCodec());
573575
socketChannel.pipeline().addLast("OnvifCodec", new OnvifCodec(getHandle()));
574576
}
@@ -583,14 +585,14 @@ public void operationComplete(@Nullable ChannelFuture future) {
583585
if (future == null) {
584586
return;
585587
}
586-
if (future.isSuccess()) {
588+
if (future.isDone() && future.isSuccess()) {
587589
Channel ch = future.channel();
588590
ch.writeAndFlush(request);
589591
} else { // an error occurred
590592
if (future.isDone() && !future.isCancelled()) {
591593
Throwable cause = future.cause();
592594
String msg = cause.getMessage();
593-
logger.debug("connect failed - cause {}", cause.getMessage());
595+
logger.debug("Connect failed - cause is: {}", cause.getMessage());
594596
if (cause instanceof ConnectTimeoutException) {
595597
usingEvents = false;// Prevent Unsubscribe from being sent
596598
ipCameraHandler.cameraCommunicationError(
@@ -601,9 +603,8 @@ public void operationComplete(@Nullable ChannelFuture future) {
601603
ipCameraHandler.cameraCommunicationError(
602604
"Camera refused to connect when using ONVIF to port:" + port);
603605
}
604-
}
605-
if (isConnected) {
606-
disconnect();
606+
} else {
607+
ipCameraHandler.cameraCommunicationError("Camera failed to connect due to being cancelled");
607608
}
608609
}
609610
}
@@ -652,7 +653,7 @@ public void gotoPreset(int index) {
652653
public void eventRecieved(String eventMessage) {
653654
String topic = Helper.fetchXML(eventMessage, "Topic", "tns1:");
654655
if (topic.isEmpty()) {
655-
sendOnvifRequest(RequestType.Renew, subscriptionXAddr);
656+
logger.debug("No ONVIF Events occured in the last 8 seconds");
656657
return;
657658
}
658659
String dataName = Helper.fetchXML(eventMessage, "tt:Data", "Name=\"");
@@ -782,7 +783,6 @@ public void eventRecieved(String eventMessage) {
782783
default:
783784
logger.debug("Please report this camera has an un-implemented ONVIF event. Topic: {}", topic);
784785
}
785-
sendOnvifRequest(RequestType.Renew, subscriptionXAddr);
786786
}
787787

788788
public boolean supportsPTZ() {

0 commit comments

Comments
 (0)