mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
Various Onvif fixes for IpCamera (#17732)
I made various changes and fixes to the ONVIF connection in the IpCamera binding. Signed-off-by: David Masshardt <david@masshardt.ch> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
f51793d25b
commit
aa13775a79
@ -209,6 +209,11 @@ If you do not specify any of these, the binding will use the default which shoul
|
||||
| `gifPreroll`| Store this many snapshots from BEFORE you trigger a GIF creation. Default: `0` will not use snapshots and will instead use a realtime stream from the ffmpegInput URL |
|
||||
| `ipWhitelist`| Enter any IPs inside brackets that you wish to allow to access the video stream. `DISABLE` the default value will turn this feature off. Example: `ipWhitelist="(127.0.0.1)(192.168.0.99)"` |
|
||||
| `ptzContinuous`| If set to false (default) the camera will move using Relative commands, If set to true the camera will instead use continuous movements and will require an `OFF` command to stop the movement. |
|
||||
| `onvifEventServiceType`| ONVIF event method to use. If camera does not report event capabilities, the event method can be forced here. |
|
||||
| | `0` - Auto detect event capabilities. (Default) ONVIF event capabilities are detected automatically. PullMessages is prefered over WSBaseNotification because there is no way to determine if an WSBaseNotification subscription exists on startup. |
|
||||
| | `1` - ONVIF events disabled. |
|
||||
| | `2` - Force ONVIF PullMessages event method even if the camera does not claim to support this. |
|
||||
| | `3` - Force ONVIF WSBaseSubscription event method even if the camera does not claim to support this. |
|
||||
|
||||
## Channels
|
||||
|
||||
|
@ -29,6 +29,7 @@ public class CameraConfig {
|
||||
private String password = "";
|
||||
public boolean useToken = true;
|
||||
private int onvifMediaProfile;
|
||||
private int onvifEventServiceType;
|
||||
private int pollTime;
|
||||
private String ffmpegInput = "";
|
||||
private String snapshotUrl = "";
|
||||
@ -54,6 +55,10 @@ public class CameraConfig {
|
||||
return onvifMediaProfile;
|
||||
}
|
||||
|
||||
public int getOnvifEventServiceType() {
|
||||
return onvifEventServiceType;
|
||||
}
|
||||
|
||||
public String getFfmpegInputOptions() {
|
||||
return ffmpegInputOptions;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.ipcamera.internal;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
@ -19,7 +20,11 @@ import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
* The {@link Helper} class has static functions that help the IpCamera binding not need as many external libs.
|
||||
@ -105,6 +110,13 @@ public class Helper {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Document loadXMLFromString(String xml) throws Exception {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(xml.getBytes());
|
||||
return builder.parse(inputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link encodeSpecialChars} Is used to replace spaces with %20 in Strings meant for URL queries.
|
||||
*
|
||||
|
@ -106,7 +106,11 @@ public class ReolinkHandler extends ChannelDuplexHandler {
|
||||
getAbilityResponse[0].error.detail);
|
||||
return;
|
||||
}
|
||||
ipCameraHandler.reolinkScheduleVersion = getAbilityResponse[0].value.ability.scheduleVersion.ver;
|
||||
if (getAbilityResponse[0].value.ability.scheduleVersion == null) {
|
||||
ipCameraHandler.logger.debug("Camera has no Schedule support.");
|
||||
} else {
|
||||
ipCameraHandler.reolinkScheduleVersion = getAbilityResponse[0].value.ability.scheduleVersion.ver;
|
||||
}
|
||||
if (getAbilityResponse[0].value.ability.supportFtpEnable == null
|
||||
|| getAbilityResponse[0].value.ability.supportFtpEnable.permit == 0) {
|
||||
ipCameraHandler.logger.debug("Camera has no Enable FTP support.");
|
||||
|
@ -61,7 +61,6 @@ import org.openhab.binding.ipcamera.internal.IpCameraDynamicStateDescriptionProv
|
||||
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;
|
||||
@ -1577,12 +1576,7 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
checkCameraConnection();
|
||||
break;
|
||||
case ONVIF_THING:
|
||||
onvifCamera.sendOnvifRequest(RequestType.Renew, onvifCamera.subscriptionXAddr);
|
||||
if (onvifCamera.pullMessageRequests.intValue() == 0) {
|
||||
logger.info("The alarm stream was not running for ONVIF camera {}, re-starting it now",
|
||||
cameraConfig.getIp());
|
||||
onvifCamera.sendOnvifRequest(RequestType.PullMessages, onvifCamera.subscriptionXAddr);
|
||||
}
|
||||
onvifCamera.checkAndRenewEventSubscription();
|
||||
break;
|
||||
case INSTAR_THING:
|
||||
checkCameraConnection();
|
||||
@ -1609,12 +1603,7 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
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);
|
||||
if (onvifCamera.pullMessageRequests.intValue() == 0) {
|
||||
logger.debug("The alarm stream was not running for Reolink camera {}, re-starting it now",
|
||||
cameraConfig.getIp());
|
||||
onvifCamera.sendOnvifRequest(RequestType.PullMessages, onvifCamera.subscriptionXAddr);
|
||||
}
|
||||
onvifCamera.checkAndRenewEventSubscription();
|
||||
}
|
||||
break;
|
||||
case DAHUA_THING:
|
||||
|
@ -53,6 +53,10 @@ public class OnvifCodec extends ChannelDuplexHandler {
|
||||
switch (response.status().code()) {
|
||||
case 200:
|
||||
break;
|
||||
case 400:
|
||||
onvifConnection.processBadRequest(requestType);
|
||||
ctx.close();
|
||||
return;
|
||||
case 401:
|
||||
if (!response.headers().isEmpty()) {
|
||||
for (CharSequence name : response.headers().names()) {
|
||||
@ -111,6 +115,7 @@ public class OnvifCodec extends ChannelDuplexHandler {
|
||||
@Override
|
||||
public void handlerRemoved(@Nullable ChannelHandlerContext ctx) {
|
||||
if (requestType == RequestType.PullMessages) {
|
||||
onvifConnection.lastPullMessageReceivedTimestamp = System.currentTimeMillis();
|
||||
onvifConnection.pullMessageRequests.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,9 @@ import org.openhab.core.types.StateOption;
|
||||
import org.openhab.core.util.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
@ -130,16 +133,18 @@ public class OnvifConnection {
|
||||
@SuppressWarnings("unused")
|
||||
private String imagingXAddr = "http://" + ipAddress + "/onvif/device_service";
|
||||
private String ptzXAddr = "http://" + ipAddress + "/onvif/ptz_service";
|
||||
public String subscriptionXAddr = "http://" + ipAddress + "/onvif/device_service";
|
||||
public String subscriptionXAddr = "";
|
||||
public String subscriptionId = "";
|
||||
private boolean isConnected = false;
|
||||
private int mediaProfileIndex = 0;
|
||||
private String rtspUri = "";
|
||||
private IpCameraHandler ipCameraHandler;
|
||||
private boolean supportsEvents = false; // camera has replied that it can do events
|
||||
// Use/skip events even if camera support them. API cameras skip, as their own methods give better results.
|
||||
private boolean usingEvents = false;
|
||||
private int onvifEventServiceType = 0; // 0 = disabled, 1 = PullMessages, 2 = WSBaseSubscription
|
||||
public AtomicInteger pullMessageRequests = new AtomicInteger();
|
||||
private long createSubscriptionTimestamp;
|
||||
public long lastPullMessageReceivedTimestamp;
|
||||
|
||||
// These hold the cameras PTZ position in the range that the camera uses, ie
|
||||
// mine is -1 to +1
|
||||
@ -228,7 +233,7 @@ public class OnvifConnection {
|
||||
case GetProfiles:
|
||||
return "<GetProfiles xmlns=\"http://www.onvif.org/ver10/media/wsdl\"/>";
|
||||
case GetServiceCapabilities:
|
||||
return "<GetServiceCapabilities xmlns=\"http://docs.oasis-open.org/wsn/b-2/\"></GetServiceCapabilities>";
|
||||
return "<GetServiceCapabilities xmlns=\"http://www.onvif.org/ver10/events/wsdl\"></GetServiceCapabilities>";
|
||||
case GetSnapshotUri:
|
||||
return "<GetSnapshotUri xmlns=\"http://www.onvif.org/ver10/media/wsdl\"><ProfileToken>"
|
||||
+ mediaProfileTokens.get(mediaProfileIndex) + "</ProfileToken></GetSnapshotUri>";
|
||||
@ -238,14 +243,14 @@ public class OnvifConnection {
|
||||
case GetSystemDateAndTime:
|
||||
return "<GetSystemDateAndTime xmlns=\"http://www.onvif.org/ver10/device/wsdl\"/>";
|
||||
case Subscribe:
|
||||
return "<Subscribe xmlns=\"http://docs.oasis-open.org/wsn/b-2/\"><ConsumerReference><Address>http://"
|
||||
return "<Subscribe xmlns=\"http://docs.oasis-open.org/wsn/b-2\" xmlns:wsa=\"http://www.w3.org/2005/08/addressing\"><ConsumerReference><wsa:Address>http://"
|
||||
+ ipCameraHandler.hostIp + ":" + SERVLET_PORT + "/ipcamera/"
|
||||
+ ipCameraHandler.getThing().getUID().getId()
|
||||
+ "/OnvifEvent</Address></ConsumerReference></Subscribe>";
|
||||
+ "/OnvifEvent</wsa:Address></ConsumerReference><InitialTerminationTime>PT600S</InitialTerminationTime></Subscribe>";
|
||||
case Unsubscribe:
|
||||
return "<Unsubscribe xmlns=\"http://docs.oasis-open.org/wsn/b-2/\"></Unsubscribe>";
|
||||
return "<Unsubscribe xmlns=\"http://docs.oasis-open.org/wsn/b-2\"></Unsubscribe>";
|
||||
case PullMessages:
|
||||
return "<PullMessages xmlns=\"http://www.onvif.org/ver10/events/wsdl\"><Timeout>PT8S</Timeout><MessageLimit>1</MessageLimit></PullMessages>";
|
||||
return "<PullMessages xmlns=\"http://www.onvif.org/ver10/events/wsdl\"><Timeout>PT8S</Timeout><MessageLimit>10</MessageLimit></PullMessages>";
|
||||
case GetEventProperties:
|
||||
return "<GetEventProperties xmlns=\"http://www.onvif.org/ver10/events/wsdl\"/>";
|
||||
case RelativeMoveLeft:
|
||||
@ -273,7 +278,7 @@ public class OnvifConnection {
|
||||
+ 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>PT10S</TerminationTime></Renew>";
|
||||
return "<Renew xmlns=\"http://docs.oasis-open.org/wsn/b-2\"><TerminationTime>PT600S</TerminationTime></Renew>";
|
||||
case GetConfigurations:
|
||||
return "<GetConfigurations xmlns=\"http://www.onvif.org/ver20/ptz/wsdl\"></GetConfigurations>";
|
||||
case GetConfigurationOptions:
|
||||
@ -314,19 +319,24 @@ public class OnvifConnection {
|
||||
logger.trace("ONVIF {} reply is: {}", requestType, message);
|
||||
switch (requestType) {
|
||||
case CreatePullPointSubscription:
|
||||
supportsEvents = true;
|
||||
subscriptionXAddr = Helper.fetchXML(message, "SubscriptionReference>", "Address>");
|
||||
int start = message.indexOf("<dom0:SubscriptionId");
|
||||
int end = message.indexOf("</dom0:SubscriptionId>");
|
||||
if (start > -1 && end > start) {
|
||||
subscriptionId = message.substring(start, end + 22);
|
||||
setSubscriptionXAddr(message);
|
||||
if (!subscriptionXAddr.isEmpty()) {
|
||||
sendOnvifRequest(RequestType.PullMessages, subscriptionXAddr);
|
||||
}
|
||||
logger.debug("subscriptionXAddr={} subscriptionId={}", subscriptionXAddr, subscriptionId);
|
||||
sendOnvifRequest(RequestType.PullMessages, subscriptionXAddr);
|
||||
break;
|
||||
case Subscribe:
|
||||
setSubscriptionXAddr(message);
|
||||
break;
|
||||
case GetCapabilities:
|
||||
parseXAddr(message);
|
||||
sendOnvifRequest(RequestType.GetProfiles, mediaXAddr);
|
||||
setOnvifEventServiceType(message.contains("WSPullPointSupport>true"),
|
||||
message.contains("WSSubscriptionPolicySupport>true"));
|
||||
if (!getEventsSupported() && ipCameraHandler.cameraConfig.getOnvifEventServiceType() != 1) {
|
||||
// If the camera does not report event capabilities here we also check with GetServiceCapabilities.
|
||||
sendOnvifRequest(RequestType.GetServiceCapabilities, mediaXAddr);
|
||||
} else {
|
||||
sendOnvifRequest(RequestType.GetProfiles, mediaXAddr);
|
||||
}
|
||||
break;
|
||||
case GetDeviceInformation:
|
||||
break;
|
||||
@ -339,14 +349,13 @@ public class OnvifConnection {
|
||||
sendPTZRequest(RequestType.GetNodes);
|
||||
}
|
||||
if (usingEvents) {// stops API cameras from getting sent ONVIF events.
|
||||
sendOnvifRequest(RequestType.GetEventProperties, eventXAddr);
|
||||
sendOnvifRequest(RequestType.GetServiceCapabilities, eventXAddr);
|
||||
createSubscription();
|
||||
}
|
||||
break;
|
||||
case GetServiceCapabilities:
|
||||
if (message.contains("WSSubscriptionPolicySupport=\"true\"")) {
|
||||
sendOnvifRequest(RequestType.Subscribe, eventXAddr);
|
||||
}
|
||||
setOnvifEventServiceType(message.contains("WSPullPointSupport=\"true\""),
|
||||
message.contains("WSSubscriptionPolicySupport=\"true\""));
|
||||
sendOnvifRequest(RequestType.GetProfiles, mediaXAddr);
|
||||
break;
|
||||
case GetSnapshotUri:
|
||||
String url = Helper.fetchXML(message, ":MediaUri", ":Uri");
|
||||
@ -378,11 +387,16 @@ public class OnvifConnection {
|
||||
parseDateAndTime(message);
|
||||
break;
|
||||
case PullMessages:
|
||||
eventRecieved(message);
|
||||
sendOnvifRequest(RequestType.PullMessages, subscriptionXAddr);
|
||||
try {
|
||||
eventRecieved(message);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error processing PullMessages error:\n{}\nmessage: {}", e.toString(), message);
|
||||
}
|
||||
if (!subscriptionXAddr.isEmpty()) {
|
||||
sendOnvifRequest(RequestType.PullMessages, subscriptionXAddr);
|
||||
}
|
||||
break;
|
||||
case GetEventProperties:
|
||||
sendOnvifRequest(RequestType.CreatePullPointSubscription, eventXAddr);
|
||||
break;
|
||||
case Renew:
|
||||
break;
|
||||
@ -409,6 +423,108 @@ public class OnvifConnection {
|
||||
}
|
||||
}
|
||||
|
||||
private void setOnvifEventServiceType(boolean cameraSupportsPullPointSupport,
|
||||
boolean cameraSupportsSubscriptionPolicySupport) {
|
||||
if (cameraSupportsPullPointSupport && ipCameraHandler.cameraConfig.getOnvifEventServiceType() == 0
|
||||
|| ipCameraHandler.cameraConfig.getOnvifEventServiceType() == 2) {
|
||||
onvifEventServiceType = 1;
|
||||
} else if (cameraSupportsSubscriptionPolicySupport
|
||||
&& ipCameraHandler.cameraConfig.getOnvifEventServiceType() == 0
|
||||
|| ipCameraHandler.cameraConfig.getOnvifEventServiceType() == 3) {
|
||||
onvifEventServiceType = 2;
|
||||
}
|
||||
}
|
||||
|
||||
public void processBadRequest(RequestType requestType) {
|
||||
logger.trace("ONVIF {} processing bad request for camera {}.", requestType, ipAddress);
|
||||
switch (requestType) {
|
||||
case CreatePullPointSubscription:
|
||||
subscriptionXAddr = "";
|
||||
logger.debug("Camera {} returned bad request on CreatePullPointSubscription. Trying again later.",
|
||||
ipAddress);
|
||||
break;
|
||||
case Subscribe:
|
||||
subscriptionXAddr = "";
|
||||
logger.debug("Camera {} returned bad request on WSBaseSubscription. Trying again later.", ipAddress);
|
||||
break;
|
||||
case GetServiceCapabilities:
|
||||
logger.debug(
|
||||
"Camera {} returned bad request on GetServiceCapabilities. Cannot auto detect supported event types.",
|
||||
ipAddress);
|
||||
sendOnvifRequest(RequestType.GetProfiles, mediaXAddr);
|
||||
break;
|
||||
case PullMessages:
|
||||
logger.debug("PullMessages returned bad request for camera {}, re-creating subscription now",
|
||||
ipAddress);
|
||||
createSubscription();
|
||||
break;
|
||||
case Renew:
|
||||
logger.debug("Renew subscription returned bad request for camera {}, re-creating subscription now",
|
||||
ipAddress);
|
||||
createSubscription();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setSubscriptionXAddr(String message) {
|
||||
subscriptionXAddr = Helper.fetchXML(message, "SubscriptionReference>", "Address>");
|
||||
int start = message.indexOf("<dom0:SubscriptionId");
|
||||
int end = message.indexOf("</dom0:SubscriptionId>");
|
||||
if (start > -1 && end > start) {
|
||||
subscriptionId = message.substring(start, end + 22);
|
||||
}
|
||||
logger.debug("subscriptionXAddr={} subscriptionId={}", subscriptionXAddr, subscriptionId);
|
||||
}
|
||||
|
||||
public void createSubscription() {
|
||||
if (!getEventsSupported()) {
|
||||
// ONVIF events are disabled or not supported.
|
||||
return;
|
||||
}
|
||||
|
||||
// Only send new subscription every 5 seconds if the camera is offline or there are already too much
|
||||
// subscriptions.
|
||||
if (createSubscriptionTimestamp == 0) {
|
||||
createSubscriptionTimestamp = System.currentTimeMillis();
|
||||
} else if (System.currentTimeMillis() - createSubscriptionTimestamp < 5000) {
|
||||
// Subscription sent less than 5 seconds ago.
|
||||
return;
|
||||
}
|
||||
|
||||
// Prefer PullPoint events over WSBaseSubscription because there is no way to check if a WSBaseSubscription is
|
||||
// already registered on the camera.
|
||||
if (onvifEventServiceType == 1) {
|
||||
sendOnvifRequest(RequestType.CreatePullPointSubscription, eventXAddr);
|
||||
} else if (onvifEventServiceType == 2) {
|
||||
sendOnvifRequest(RequestType.Subscribe, eventXAddr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be executed regularly to renew the event subscription and to check if a new subscription is
|
||||
* needed.
|
||||
*/
|
||||
public void checkAndRenewEventSubscription() {
|
||||
if (getEventsSupported()) {
|
||||
// If we get events via PullMessages check if a PullMessages request is running or we just received an
|
||||
// answer in the last second. If this is not the case create a new PullMessages subscription.
|
||||
if (onvifEventServiceType == 1 && pullMessageRequests.intValue() == 0
|
||||
&& System.currentTimeMillis() - lastPullMessageReceivedTimestamp > 1000) {
|
||||
logger.debug("The alarm stream was not running for camera {}, re-starting it now", ipAddress);
|
||||
createSubscription();
|
||||
} else if (!subscriptionXAddr.isEmpty()) {
|
||||
// Renew the active subscription.
|
||||
sendOnvifRequest(RequestType.Renew, subscriptionXAddr);
|
||||
} else {
|
||||
// The camera claims to have event support, but no subscription was created yet. Try to create a new
|
||||
// subscription.
|
||||
createSubscription();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link removeIPandPortFromUrl} Will throw away all text before the cameras IP, also removes the IP and the
|
||||
* PORT
|
||||
@ -460,7 +576,7 @@ public class OnvifConnection {
|
||||
}
|
||||
temp = Helper.fetchXML(message, "<tt:Events", "tt:XAddr");
|
||||
if (!temp.isEmpty()) {
|
||||
subscriptionXAddr = eventXAddr = temp;
|
||||
eventXAddr = temp;
|
||||
logger.debug("eventsXAddr: {}", eventXAddr);
|
||||
}
|
||||
temp = Helper.fetchXML(message, "<tt:Media", "tt:XAddr");
|
||||
@ -698,147 +814,178 @@ public class OnvifConnection {
|
||||
}
|
||||
|
||||
public void eventRecieved(String eventMessage) {
|
||||
String topic = Helper.fetchXML(eventMessage, "Topic", "tns1:");
|
||||
if (topic.isEmpty()) {
|
||||
logger.trace("No ONVIF Events occured in the last 8 seconds");
|
||||
Document xmlDocument;
|
||||
try {
|
||||
xmlDocument = Helper.loadXMLFromString(eventMessage);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error parsing ONVIF xml.", e);
|
||||
return;
|
||||
}
|
||||
String dataName = Helper.fetchXML(eventMessage, "tt:Data", "Name=\"");
|
||||
String dataValue = Helper.fetchXML(eventMessage, "tt:Data", "Value=\"");
|
||||
logger.debug("ONVIF Event Topic: {}, Data: {}, Value: {}", topic, dataName, dataValue);
|
||||
switch (topic) {
|
||||
case "RuleEngine/CellMotionDetector/Motion":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_CELL_MOTION_ALARM);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_CELL_MOTION_ALARM);
|
||||
}
|
||||
break;
|
||||
case "VideoAnalytics/Motion":
|
||||
if ("Trigger".equals(dataValue)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
|
||||
} else if ("Normal".equals(dataValue)) {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/tnsaxis:VMD3/vmd3_video_1":
|
||||
case "RuleEngine/MotionRegionDetector/Motion":
|
||||
case "VideoSource/MotionAlarm":
|
||||
if ("true".equals(dataValue) || "1".equals(dataValue)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
|
||||
} else if ("false".equals(dataValue) || "0".equals(dataValue)) {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
|
||||
}
|
||||
break;
|
||||
case "AudioAnalytics/Audio/DetectedSound":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.audioDetected();
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.noAudioDetected();
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/FieldDetector/ObjectsInside":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_FIELD_DETECTION_ALARM);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/LineDetector/Crossed":
|
||||
if ("ObjectId".equals(dataName)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM);
|
||||
} else {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/TamperDetector/Tamper":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TAMPER_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TAMPER_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "Device/tnsaxis:HardwareFailure/StorageFailure":
|
||||
case "Device/HardwareFailure/StorageFailure":
|
||||
if ("true".equals(dataValue) || "1".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_STORAGE_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue) || "0".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_STORAGE_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "VideoSource/ImageTooDark/AnalyticsService":
|
||||
case "VideoSource/ImageTooDark/ImagingService":
|
||||
case "VideoSource/ImageTooDark/RecordingService":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_DARK_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_DARK_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "VideoSource/GlobalSceneChange/AnalyticsService":
|
||||
case "VideoSource/GlobalSceneChange/ImagingService":
|
||||
case "VideoSource/GlobalSceneChange/RecordingService":
|
||||
if ("true".equals(dataValue) || "1".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue) || "0".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "VideoSource/ImageTooBright/AnalyticsService":
|
||||
case "VideoSource/ImageTooBright/ImagingService":
|
||||
case "VideoSource/ImageTooBright/RecordingService":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_BRIGHT_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_BRIGHT_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "VideoSource/ImageTooBlurry/AnalyticsService":
|
||||
case "VideoSource/ImageTooBlurry/ImagingService":
|
||||
case "VideoSource/ImageTooBlurry/RecordingService":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/Visitor":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_DOORBELL, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_DOORBELL, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/VehicleDetect":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_CAR_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_CAR_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/DogCatDetect":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_ANIMAL_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_ANIMAL_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/FaceDetect":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_FACE_DETECTED, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_FACE_DETECTED, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/PeopleDetect":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_HUMAN_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_HUMAN_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Please report this camera has an un-implemented ONVIF event. Topic: {}", topic);
|
||||
NodeList NotificationMessageNodeList = xmlDocument.getElementsByTagName("wsnt:NotificationMessage");
|
||||
for (int i = 0; i < NotificationMessageNodeList.getLength(); i++) {
|
||||
Element notificationMessageElement = (Element) NotificationMessageNodeList.item(i);
|
||||
|
||||
Element topicElement = (Element) notificationMessageElement.getElementsByTagName("wsnt:Topic").item(0);
|
||||
String topic = topicElement.getFirstChild().getNodeValue().replace("tns1:", "");
|
||||
|
||||
String sourceName = "";
|
||||
String sourceValue = "";
|
||||
Element sourceElement = (Element) notificationMessageElement.getElementsByTagName("tt:Source").item(0);
|
||||
|
||||
if (sourceElement != null) {
|
||||
Element sourceItemElement = (Element) sourceElement.getElementsByTagName("tt:SimpleItem").item(0);
|
||||
sourceName = sourceItemElement.getAttributes().getNamedItem("Name").getNodeValue();
|
||||
sourceValue = sourceItemElement.getAttributes().getNamedItem("Value").getNodeValue();
|
||||
}
|
||||
|
||||
Element dataElement = (Element) notificationMessageElement.getElementsByTagName("tt:Data").item(0);
|
||||
|
||||
if (dataElement == null) {
|
||||
// Events without data element are not relevant.
|
||||
continue;
|
||||
}
|
||||
|
||||
Element dataItemElement = (Element) dataElement.getElementsByTagName("tt:SimpleItem").item(0);
|
||||
|
||||
String dataName = dataItemElement.getAttributes().getNamedItem("Name").getNodeValue();
|
||||
String dataValue = dataItemElement.getAttributes().getNamedItem("Value").getNodeValue();
|
||||
|
||||
logger.debug("ONVIF Event Topic: {}, Source name: {}, Source value: {}, Data name: {}, Data value: {}",
|
||||
topic, sourceName, sourceValue, dataName, dataValue);
|
||||
switch (topic) {
|
||||
case "RuleEngine/CellMotionDetector/Motion":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_CELL_MOTION_ALARM);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_CELL_MOTION_ALARM);
|
||||
}
|
||||
break;
|
||||
case "VideoAnalytics/Motion":
|
||||
if ("Trigger".equals(dataValue)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
|
||||
} else if ("Normal".equals(dataValue)) {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/tnsaxis:VMD3/vmd3_video_1":
|
||||
case "RuleEngine/MotionRegionDetector/Motion":
|
||||
case "VideoSource/MotionAlarm":
|
||||
if ("true".equals(dataValue) || "1".equals(dataValue)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
|
||||
} else if ("false".equals(dataValue) || "0".equals(dataValue)) {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
|
||||
}
|
||||
break;
|
||||
case "AudioAnalytics/Audio/DetectedSound":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.audioDetected();
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.noAudioDetected();
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/FieldDetector/ObjectsInside":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_FIELD_DETECTION_ALARM);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/LineDetector/Crossed":
|
||||
if ("ObjectId".equals(dataName)) {
|
||||
ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM);
|
||||
} else {
|
||||
ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/TamperDetector/Tamper":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TAMPER_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TAMPER_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "Device/tnsaxis:HardwareFailure/StorageFailure":
|
||||
case "Device/HardwareFailure/StorageFailure":
|
||||
if ("true".equals(dataValue) || "1".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_STORAGE_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue) || "0".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_STORAGE_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "VideoSource/ImageTooDark/AnalyticsService":
|
||||
case "VideoSource/ImageTooDark/ImagingService":
|
||||
case "VideoSource/ImageTooDark/RecordingService":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_DARK_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_DARK_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "VideoSource/GlobalSceneChange/AnalyticsService":
|
||||
case "VideoSource/GlobalSceneChange/ImagingService":
|
||||
case "VideoSource/GlobalSceneChange/RecordingService":
|
||||
if ("true".equals(dataValue) || "1".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue) || "0".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "VideoSource/ImageTooBright/AnalyticsService":
|
||||
case "VideoSource/ImageTooBright/ImagingService":
|
||||
case "VideoSource/ImageTooBright/RecordingService":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_BRIGHT_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_BRIGHT_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "VideoSource/ImageTooBlurry/AnalyticsService":
|
||||
case "VideoSource/ImageTooBlurry/ImagingService":
|
||||
case "VideoSource/ImageTooBlurry/RecordingService":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/Visitor":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_DOORBELL, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_DOORBELL, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/VehicleDetect":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_CAR_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_CAR_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/DogCatDetect":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_ANIMAL_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_ANIMAL_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/FaceDetect":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_FACE_DETECTED, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_FACE_DETECTED, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "RuleEngine/MyRuleDetector/PeopleDetect":
|
||||
if ("true".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_HUMAN_ALARM, OnOffType.ON);
|
||||
} else if ("false".equals(dataValue)) {
|
||||
ipCameraHandler.changeAlarmState(CHANNEL_HUMAN_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Please report this camera has an un-implemented ONVIF event. Topic: {}", topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1018,7 +1165,7 @@ public class OnvifConnection {
|
||||
}
|
||||
|
||||
public boolean getEventsSupported() {
|
||||
return supportsEvents;
|
||||
return onvifEventServiceType > 0;
|
||||
}
|
||||
|
||||
public void setIsConnected(boolean isConnected) {
|
||||
@ -1049,7 +1196,8 @@ public class OnvifConnection {
|
||||
connecting.lock();// Lock out multiple disconnect()/connect() attempts as we try to send Unsubscribe.
|
||||
try {
|
||||
if (bootstrap != null) {
|
||||
if (isConnected && usingEvents && !mainEventLoopGroup.isShuttingDown()) {
|
||||
if (isConnected && usingEvents && !mainEventLoopGroup.isShuttingDown()
|
||||
&& !subscriptionXAddr.isEmpty()) {
|
||||
// Only makes sense to send if connected
|
||||
// Some cameras may continue to send events even when they can't reach a server.
|
||||
sendOnvifRequest(RequestType.Unsubscribe, subscriptionXAddr);
|
||||
|
@ -79,7 +79,9 @@ public class CameraServlet extends IpCameraServlet {
|
||||
snapshotData.close();
|
||||
break;
|
||||
case "/OnvifEvent":
|
||||
handler.onvifCamera.eventRecieved(req.getReader().toString());
|
||||
ServletInputStream inputStream = req.getInputStream();
|
||||
String xmlData = new String(inputStream.readAllBytes(), "UTF-8");
|
||||
handler.onvifCamera.eventRecieved(xmlData);
|
||||
break;
|
||||
default:
|
||||
logger.debug("Recieved unknown request \tPOST:{}", pathInfo);
|
||||
|
@ -526,6 +526,12 @@ thing-type.config.ipcamera.onvif.onvifMediaProfile.label = ONVIF Media Profile
|
||||
thing-type.config.ipcamera.onvif.onvifMediaProfile.description = Cameras can supply more than one stream at different resolutions and formats. 0 selects the main-stream and 1 or above are the sub-streams. Sometimes you need to turn on sub-streams in the cameras setup before they can be used.
|
||||
thing-type.config.ipcamera.onvif.onvifPort.label = ONVIF Port
|
||||
thing-type.config.ipcamera.onvif.onvifPort.description = The port your camera uses for ONVIF connections. This is needed for PTZ movement, alarm events and auto discovery of RTSP and snapshot URLs.
|
||||
thing-type.config.ipcamera.onvif.onvifEventServiceType.label = ONVIF event method
|
||||
thing-type.config.ipcamera.onvif.onvifEventServiceType.description = ONVIF event method to use. If camera does not report event capabilities, the event method can be forced here.
|
||||
thing-type.config.ipcamera.onvif.onvifEventServiceType.option.0 = Auto detect (0)
|
||||
thing-type.config.ipcamera.onvif.onvifEventServiceType.option.1 = Disabled (1)
|
||||
thing-type.config.ipcamera.onvif.onvifEventServiceType.option.2 = Force PullMessages (2)
|
||||
thing-type.config.ipcamera.onvif.onvifEventServiceType.option.3 = Force WSBaseSubscription (3)
|
||||
thing-type.config.ipcamera.onvif.password.label = Password
|
||||
thing-type.config.ipcamera.onvif.password.description = Enter the password for your camera. Leave blank if your camera does not use one.
|
||||
thing-type.config.ipcamera.onvif.pollTime.label = Poll Time
|
||||
@ -590,6 +596,12 @@ thing-type.config.ipcamera.reolink.onvifMediaProfile.label = ONVIF Media Profile
|
||||
thing-type.config.ipcamera.reolink.onvifMediaProfile.description = Cameras can supply more than one stream at different resolutions and formats. 0 selects the main-stream and 1 or above are the sub-streams. Sometimes you need to turn on sub-streams in the cameras setup before they can be used.
|
||||
thing-type.config.ipcamera.reolink.onvifPort.label = ONVIF Port
|
||||
thing-type.config.ipcamera.reolink.onvifPort.description = The port your camera uses for ONVIF connections. This is needed for PTZ movement, alarm events and auto discovery of RTSP and snapshot URLs.
|
||||
thing-type.config.ipcamera.reolink.onvifEventServiceType.label = ONVIF event method
|
||||
thing-type.config.ipcamera.reolink.onvifEventServiceType.description = ONVIF event method to use. If camera does not report event capabilities, the event method can be forced here.
|
||||
thing-type.config.ipcamera.reolink.onvifEventServiceType.option.0 = Auto detect (0)
|
||||
thing-type.config.ipcamera.reolink.onvifEventServiceType.option.1 = Disabled (1)
|
||||
thing-type.config.ipcamera.reolink.onvifEventServiceType.option.2 = Force PullMessages (2)
|
||||
thing-type.config.ipcamera.reolink.onvifEventServiceType.option.3 = Force WSBaseSubscription (3)
|
||||
thing-type.config.ipcamera.reolink.password.label = Password
|
||||
thing-type.config.ipcamera.reolink.password.description = Enter the password for your camera. Leave blank if your camera does not use one.
|
||||
thing-type.config.ipcamera.reolink.pollTime.label = Poll Time
|
||||
|
@ -548,6 +548,21 @@
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="onvifEventServiceType" type="integer" groupName="Settings" multiple="false">
|
||||
<label>ONVIF event method:</label>
|
||||
<description>ONVIF event method to use. If camera does not report event capabilities, the event method can be forced
|
||||
here.
|
||||
</description>
|
||||
<default>0</default>
|
||||
<advanced>true</advanced>
|
||||
<options>
|
||||
<option value="0">Auto detect (0)</option>
|
||||
<option value="1">Disabled (1)</option>
|
||||
<option value="2">Force PullMessages (2)</option>
|
||||
<option value="3">Force WSBaseSubscription (3)</option>
|
||||
</options>
|
||||
</parameter>
|
||||
|
||||
<parameter name="ipWhitelist" type="text" required="false" groupName="Settings">
|
||||
<label>IP Whitelist</label>
|
||||
<description>Enter any IP's inside (brackets) that you wish to allow to access the video stream. 'DISABLE' will
|
||||
@ -2572,6 +2587,21 @@
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="onvifEventServiceType" type="integer" groupName="Settings" multiple="false">
|
||||
<label>ONVIF event method:</label>
|
||||
<description>ONVIF event method to use. If camera does not report event capabilities, the event method can be forced
|
||||
here.
|
||||
</description>
|
||||
<default>0</default>
|
||||
<advanced>true</advanced>
|
||||
<options>
|
||||
<option value="0">Auto detect (0)</option>
|
||||
<option value="1">Disabled (1)</option>
|
||||
<option value="2">Force PullMessages (2)</option>
|
||||
<option value="3">Force WSBaseSubscription (3)</option>
|
||||
</options>
|
||||
</parameter>
|
||||
|
||||
<parameter name="pollTime" type="integer" required="true" min="1000" groupName="Settings" unit="ms">
|
||||
<label>Poll Time</label>
|
||||
<description>Most features are made on demand and not polled, but some features require a regular snapshot to work.
|
||||
|
Loading…
Reference in New Issue
Block a user