[ipcamera] Add Reolink API support (#14728)
Signed-off-by: Matthew Skinner <matt@pcmus.com>
This commit is contained in:
@@ -27,6 +27,7 @@ public class CameraConfig {
|
||||
private int onvifPort;
|
||||
private String username = "";
|
||||
private String password = "";
|
||||
public boolean useToken = true;
|
||||
private int onvifMediaProfile;
|
||||
private int pollTime;
|
||||
private String ffmpegInput = "";
|
||||
|
||||
@@ -107,7 +107,6 @@ public class FoscamHandler extends ChannelDuplexHandler {
|
||||
|
||||
if (content.contains("</CGI_Result>")) {
|
||||
ctx.close();
|
||||
ipCameraHandler.logger.debug("End of FOSCAM handler reached, so closing the channel to the camera now");
|
||||
}
|
||||
} finally {
|
||||
ReferenceCountUtil.release(msg);
|
||||
|
||||
@@ -33,6 +33,7 @@ public class IpCameraBindingConstants {
|
||||
public static final String AMCREST_HANDLER = "amcrestHandler";
|
||||
public static final String COMMON_HANDLER = "commonHandler";
|
||||
public static final String INSTAR_HANDLER = "instarHandler";
|
||||
public static final String REOLINK_HANDLER = "reolinkHandler";
|
||||
|
||||
public static enum FFmpegFormat {
|
||||
HLS,
|
||||
@@ -66,10 +67,12 @@ public class IpCameraBindingConstants {
|
||||
public static final ThingTypeUID THING_TYPE_DAHUA = new ThingTypeUID(BINDING_ID, DAHUA_THING);
|
||||
public static final String DOORBIRD_THING = "doorbird";
|
||||
public static final ThingTypeUID THING_TYPE_DOORBIRD = new ThingTypeUID(BINDING_ID, DOORBIRD_THING);
|
||||
public static final String REOLINK_THING = "reolink";
|
||||
public static final ThingTypeUID THING_TYPE_REOLINK = new ThingTypeUID(BINDING_ID, REOLINK_THING);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<ThingTypeUID>(
|
||||
Arrays.asList(THING_TYPE_ONVIF, THING_TYPE_GENERIC, THING_TYPE_AMCREST, THING_TYPE_DAHUA, THING_TYPE_INSTAR,
|
||||
THING_TYPE_FOSCAM, THING_TYPE_DOORBIRD, THING_TYPE_HIKVISION));
|
||||
THING_TYPE_FOSCAM, THING_TYPE_DOORBIRD, THING_TYPE_HIKVISION, THING_TYPE_REOLINK));
|
||||
|
||||
public static final Set<ThingTypeUID> GROUP_SUPPORTED_THING_TYPES = new HashSet<ThingTypeUID>(
|
||||
Arrays.asList(THING_TYPE_GROUP));
|
||||
@@ -139,4 +142,6 @@ public class IpCameraBindingConstants {
|
||||
public static final String CHANNEL_CAR_ALARM = "carAlarm";
|
||||
public static final String CHANNEL_HUMAN_ALARM = "humanAlarm";
|
||||
public static final String CHANNEL_ANIMAL_ALARM = "animalAlarm";
|
||||
public static final String CHANNEL_ENABLE_FTP = "enableFTP";
|
||||
public static final String CHANNEL_ENABLE_RECORDINGS = "enableRecordings";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ipcamera.internal;
|
||||
|
||||
import static org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.ipcamera.internal.ReolinkState.GetAiStateResponse;
|
||||
import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
/**
|
||||
* The {@link ReolinkHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class ReolinkHandler extends ChannelDuplexHandler {
|
||||
protected final Gson gson = new Gson();
|
||||
private IpCameraHandler ipCameraHandler;
|
||||
private String requestUrl = "Empty";
|
||||
|
||||
public ReolinkHandler(IpCameraHandler thingHandler) {
|
||||
ipCameraHandler = thingHandler;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (msg == null || ctx == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String content = msg.toString();
|
||||
ipCameraHandler.logger.trace("HTTP Result from {} contains \t:{}:", requestUrl, content);
|
||||
int afterCommand = requestUrl.indexOf("&");
|
||||
String cutDownURL;
|
||||
if (afterCommand < 0) {
|
||||
cutDownURL = requestUrl;
|
||||
} else {
|
||||
cutDownURL = requestUrl.substring(0, afterCommand);
|
||||
}
|
||||
switch (cutDownURL) {// Use a cutdown URL as we can not use variables in a switch()
|
||||
case "/api.cgi?cmd=Login":
|
||||
ipCameraHandler.reolinkAuth = "&token=" + Helper.searchString(content, "\"name\" : \"");
|
||||
if (ipCameraHandler.reolinkAuth.length() > 7) {
|
||||
ipCameraHandler.logger.debug("Your Reolink camera gave a login:{}",
|
||||
ipCameraHandler.reolinkAuth);
|
||||
ipCameraHandler.snapshotUri = "/cgi-bin/api.cgi?cmd=Snap&channel="
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel() + "&rs=openHAB"
|
||||
+ ipCameraHandler.reolinkAuth;
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=GetAbility" + ipCameraHandler.reolinkAuth,
|
||||
"[{ \"cmd\":\"GetAbility\", \"param\":{ \"User\":{ \"userName\":\""
|
||||
+ ipCameraHandler.cameraConfig.getUser() + "\" }}}]");
|
||||
} else {
|
||||
ipCameraHandler.logger.info("Your Reolink camera gave a bad login response:{}", content);
|
||||
}
|
||||
break;
|
||||
case "/api.cgi?cmd=GetAbility": // Used to check what channels the camera supports
|
||||
List<org.openhab.core.thing.Channel> removeChannels = new ArrayList<>();
|
||||
org.openhab.core.thing.Channel channel;
|
||||
if (content.contains("\"supportFtpEnable\": { \"permit\": 0")) {
|
||||
ipCameraHandler.logger.debug("Camera has no Enable FTP support.");
|
||||
channel = ipCameraHandler.getThing().getChannel(CHANNEL_ENABLE_FTP);
|
||||
if (channel != null) {
|
||||
removeChannels.add(channel);
|
||||
}
|
||||
}
|
||||
if (content.contains("\"supportRecordEnable\": { \"permit\": 0")) {
|
||||
ipCameraHandler.logger.debug("Camera has no enable recording support.");
|
||||
channel = ipCameraHandler.getThing().getChannel(CHANNEL_ENABLE_RECORDINGS);
|
||||
if (channel != null) {
|
||||
removeChannels.add(channel);
|
||||
}
|
||||
}
|
||||
if (content.contains("\"floodLight\": { \"permit\": 0")) {
|
||||
ipCameraHandler.logger.debug("Camera has no Flood light support.");
|
||||
channel = ipCameraHandler.getThing().getChannel(CHANNEL_ENABLE_LED);
|
||||
if (channel != null) {
|
||||
removeChannels.add(channel);
|
||||
}
|
||||
}
|
||||
ipCameraHandler.removeChannels(removeChannels);
|
||||
break;
|
||||
case "/api.cgi?cmd=GetAiState":
|
||||
ipCameraHandler.setChannelState(CHANNEL_LAST_EVENT_DATA, new StringType(content));
|
||||
GetAiStateResponse[] aiResponse = gson.fromJson(content, GetAiStateResponse[].class);
|
||||
if (aiResponse == null) {
|
||||
ipCameraHandler.logger.debug("The GetAiStateResponse could not be parsed");
|
||||
return;
|
||||
}
|
||||
if (aiResponse[0].value.dog_cat != null) {
|
||||
if (aiResponse[0].value.dog_cat.alarm_state == 1) {
|
||||
ipCameraHandler.setChannelState(CHANNEL_ANIMAL_ALARM, OnOffType.ON);
|
||||
} else {
|
||||
ipCameraHandler.setChannelState(CHANNEL_ANIMAL_ALARM, OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
if (aiResponse[0].value.face.alarm_state == 1) {
|
||||
ipCameraHandler.setChannelState(CHANNEL_FACE_DETECTED, OnOffType.ON);
|
||||
} else {
|
||||
ipCameraHandler.setChannelState(CHANNEL_FACE_DETECTED, OnOffType.OFF);
|
||||
}
|
||||
if (aiResponse[0].value.people.alarm_state == 1) {
|
||||
ipCameraHandler.setChannelState(CHANNEL_HUMAN_ALARM, OnOffType.ON);
|
||||
} else {
|
||||
ipCameraHandler.setChannelState(CHANNEL_HUMAN_ALARM, OnOffType.OFF);
|
||||
}
|
||||
if (aiResponse[0].value.vehicle.alarm_state == 1) {
|
||||
ipCameraHandler.setChannelState(CHANNEL_CAR_ALARM, OnOffType.ON);
|
||||
} else {
|
||||
ipCameraHandler.setChannelState(CHANNEL_CAR_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "/api.cgi?cmd=GetAudioAlarmV20":
|
||||
if (content.contains("\"enable\" : 1")) {
|
||||
ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.ON);
|
||||
} else {
|
||||
ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.OFF);
|
||||
}
|
||||
break;
|
||||
case "/api.cgi?cmd=GetIrLights":
|
||||
if (content.contains("\"state\" : 0")) {
|
||||
ipCameraHandler.setChannelState(CHANNEL_AUTO_LED, OnOffType.OFF);
|
||||
} else {
|
||||
ipCameraHandler.setChannelState(CHANNEL_AUTO_LED, OnOffType.ON);
|
||||
}
|
||||
break;
|
||||
case "/api.cgi?cmd=GetMdState":
|
||||
if (content.contains("\"state\" : 0")) {
|
||||
ipCameraHandler.setChannelState(CHANNEL_MOTION_ALARM, OnOffType.OFF);
|
||||
} else {
|
||||
ipCameraHandler.setChannelState(CHANNEL_MOTION_ALARM, OnOffType.ON);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
ReferenceCountUtil.release(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// This handles the commands that come from the openHAB event bus.
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_ENABLE_MOTION_ALARM:
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=GetMdState" + ipCameraHandler.reolinkAuth);
|
||||
break;
|
||||
case CHANNEL_ENABLE_AUDIO_ALARM:
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=GetAudioAlarmV20" + ipCameraHandler.reolinkAuth,
|
||||
"[{ \"cmd\":\"GetAudioAlarmV20\", \"action\":1, \"param\":{ \"channel\": 0}}]");
|
||||
break;
|
||||
case CHANNEL_AUTO_LED:
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=GetIrLights" + ipCameraHandler.reolinkAuth,
|
||||
"[{ \"cmd\":\"GetIrLights\"}]");
|
||||
break;
|
||||
}
|
||||
return;
|
||||
} // end of "REFRESH"
|
||||
switch (channelUID.getId()) {
|
||||
case CHANNEL_ACTIVATE_ALARM_OUTPUT: // cameras built in siren
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=AudioAlarmPlay" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\": \"AudioAlarmPlay\", \"param\": {\"alarm_mode\": \"manul\", \"manual_switch\": 1, \"channel\": "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel() + " }}]");
|
||||
} else {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=AudioAlarmPlay" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\": \"AudioAlarmPlay\", \"param\": {\"alarm_mode\": \"manul\", \"manual_switch\": 0, \"channel\": "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel() + " }}]");
|
||||
}
|
||||
break;
|
||||
case CHANNEL_AUTO_LED:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetIrLights" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\": \"SetIrLights\",\"action\": 0,\"param\": {\"IrLights\": {\"channel\": "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel() + ",\"state\": \"Auto\"}}}]");
|
||||
} else {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetIrLights" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\": \"SetIrLights\",\"action\": 0,\"param\": {\"IrLights\": {\"channel\": "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel() + ",\"state\": \"Off\"}}}]");
|
||||
}
|
||||
break;
|
||||
case CHANNEL_ENABLE_AUDIO_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetAudioAlarm" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\": \" SetAudioAlarm\",\"param\": {\"Audio\": {\"schedule\": {\"enable\": 1,\"table\": \"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\"}}}}]");
|
||||
} else {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetAudioAlarm" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\": \" SetAudioAlarm\",\"param\": {\"Audio\": {\"schedule\": {\"enable\": 0,\"table\": \"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\"}}}}]");
|
||||
}
|
||||
break;
|
||||
case CHANNEL_ENABLE_FTP:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetFtp" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\":\"SetFtp\",\"param\":{\"Rec\" : {\"channel\" : "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel()
|
||||
+ ",\"schedule\" : {\"enable\" : 1}}}}]");
|
||||
} else {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetFtp" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\":\"SetFtp\",\"param\":{\"Rec\" : {\"channel\" : "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel()
|
||||
+ ",\"schedule\" : {\"enable\" : 0}}}}]");
|
||||
}
|
||||
break;
|
||||
case CHANNEL_ENABLE_LED:
|
||||
if (OnOffType.OFF.equals(command) || PercentType.ZERO.equals(command)) {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetWhiteLed" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\": \"SetWhiteLed\",\"param\": {\"WhiteLed\": {\"state\": 0,\"channel\": "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel() + ",\"mode\": 1}}}]");
|
||||
} else if (OnOffType.ON.equals(command)) {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetWhiteLed" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\": \"SetWhiteLed\",\"param\": {\"WhiteLed\": {\"state\": 1,\"channel\": "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel() + ",\"mode\": 1}}}]");
|
||||
} else if (command instanceof PercentType) {
|
||||
int value = ((PercentType) command).toBigDecimal().intValue();
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetWhiteLed" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\": \"SetWhiteLed\",\"param\": {\"WhiteLed\": {\"state\": 1,\"channel\": "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel() + ",\"mode\": 1,\"bright\": " + value
|
||||
+ "}}}]");
|
||||
}
|
||||
case CHANNEL_ENABLE_MOTION_ALARM:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetMdAlarm" + ipCameraHandler.reolinkAuth);
|
||||
} else {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetMdAlarm" + ipCameraHandler.reolinkAuth);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_ENABLE_RECORDINGS:
|
||||
if (OnOffType.ON.equals(command)) {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetRec" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\":\"SetRec\",\"param\":{\"Rec\" : {\"channel\" : "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel()
|
||||
+ ",\"schedule\" : {\"enable\" : 1}}}}]");
|
||||
} else {
|
||||
ipCameraHandler.sendHttpPOST("/api.cgi?cmd=SetRec" + ipCameraHandler.reolinkAuth,
|
||||
"[{\"cmd\":\"SetRec\",\"param\":{\"Rec\" : {\"channel\" : "
|
||||
+ ipCameraHandler.cameraConfig.getNvrChannel()
|
||||
+ ",\"schedule\" : {\"enable\" : 0}}}}]");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If a camera does not need to poll a request as often as snapshots, it can be
|
||||
// added here. Binding steps through the list.
|
||||
public List<String> getLowPriorityRequests() {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ipcamera.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ReolinkState} class holds the state and GSON parsed replies for a single Reolink Camera.
|
||||
*
|
||||
* @author Matthew Skinner - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ReolinkState {
|
||||
public class GetAiStateResponse {
|
||||
public class Value {
|
||||
public class Alarm {
|
||||
public int alarm_state = 0;
|
||||
public int support = 0;
|
||||
}
|
||||
|
||||
public int channel = 0;
|
||||
public Alarm dog_cat = new Alarm();
|
||||
public Alarm face = new Alarm();
|
||||
public Alarm people = new Alarm();
|
||||
public Alarm vehicle = new Alarm();
|
||||
}
|
||||
|
||||
public String cmd = "";
|
||||
public int code = 0;
|
||||
public Value value = new Value();
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import java.math.BigDecimal;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
@@ -57,6 +58,7 @@ import org.openhab.binding.ipcamera.internal.IpCameraActions;
|
||||
import org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.FFmpegFormat;
|
||||
import org.openhab.binding.ipcamera.internal.IpCameraDynamicStateDescriptionProvider;
|
||||
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.servlet.CameraServlet;
|
||||
import org.openhab.core.OpenHAB;
|
||||
@@ -72,9 +74,11 @@ import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.service.http.HttpService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -146,10 +150,11 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
private @Nullable ScheduledFuture<?> cameraConnectionJob = null;
|
||||
private @Nullable ScheduledFuture<?> pollCameraJob = null;
|
||||
private @Nullable ScheduledFuture<?> snapshotJob = null;
|
||||
private @Nullable ScheduledFuture<?> authenticationJob = null;
|
||||
private @Nullable Bootstrap mainBootstrap;
|
||||
private EventLoopGroup mainEventLoopGroup = new NioEventLoopGroup(1);
|
||||
private FullHttpRequest putRequestWithBody = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, new HttpMethod("PUT"),
|
||||
"");
|
||||
private FullHttpRequest putRequestWithBody = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "");
|
||||
private FullHttpRequest postRequestWithBody = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "");
|
||||
private String gifFilename = "ipcamera";
|
||||
private String gifHistory = "";
|
||||
private String mp4History = "";
|
||||
@@ -168,6 +173,7 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
|
||||
// basicAuth MUST remain private as it holds the cameraConfig.getPassword()
|
||||
private String basicAuth = "";
|
||||
public String reolinkAuth = "&token=null";
|
||||
public boolean useBasicAuth = false;
|
||||
public boolean useDigestAuth = false;
|
||||
public boolean newInstarApi = false;
|
||||
@@ -325,9 +331,7 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
}
|
||||
// Foscam needs this as will other cameras with chunks//
|
||||
if (isChunked && bytesAlreadyRecieved != 0) {
|
||||
logger.debug("Reply is chunked.");
|
||||
reply = incomingMessage;
|
||||
super.channelRead(ctx, reply);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,7 +339,7 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
// Foscam cameras need this
|
||||
if (!contentType.contains("image/jp") && bytesAlreadyRecieved != 0) {
|
||||
reply = incomingMessage;
|
||||
logger.debug("Packet back from camera is {}", incomingMessage);
|
||||
logger.trace("Packet back from camera is {}", incomingMessage);
|
||||
super.channelRead(ctx, reply);
|
||||
}
|
||||
}
|
||||
@@ -472,6 +476,24 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
sendHttpRequest("PUT", httpRequestURL, null);
|
||||
}
|
||||
|
||||
public void sendHttpPOST(String httpPostURL, String content) {
|
||||
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, httpPostURL);
|
||||
request.headers().set("Host", cameraConfig.getIp());
|
||||
request.headers().add("Content-Type", "application/json");
|
||||
request.headers().add("User-Agent",
|
||||
"openHAB/" + FrameworkUtil.getBundle(this.getClass()).getVersion().toString());
|
||||
request.headers().add("Accept", "*/*");
|
||||
ByteBuf bbuf = Unpooled.copiedBuffer(content, StandardCharsets.UTF_8);
|
||||
request.headers().set("Content-Length", bbuf.readableBytes());
|
||||
request.content().clear().writeBytes(bbuf);
|
||||
postRequestWithBody = request; // use Global so the authhandler can use it when resent with DIGEST.
|
||||
sendHttpRequest("POST", httpPostURL, null);
|
||||
}
|
||||
|
||||
public void sendHttpPOST(String httpRequestURL) {
|
||||
sendHttpRequest("POST", httpRequestURL, null);
|
||||
}
|
||||
|
||||
public void sendHttpGET(String httpRequestURL) {
|
||||
sendHttpRequest("GET", httpRequestURL, null);
|
||||
}
|
||||
@@ -566,6 +588,9 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
case INSTAR_THING:
|
||||
socketChannel.pipeline().addLast(INSTAR_HANDLER, new InstarHandler(getHandle()));
|
||||
break;
|
||||
case REOLINK_THING:
|
||||
socketChannel.pipeline().addLast(REOLINK_HANDLER, new ReolinkHandler(getHandle()));
|
||||
break;
|
||||
default:
|
||||
socketChannel.pipeline().addLast(new HttpOnlyHandler(getHandle()));
|
||||
break;
|
||||
@@ -575,12 +600,14 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
}
|
||||
|
||||
FullHttpRequest request;
|
||||
if (!"PUT".equals(httpMethod) || (useDigestAuth && digestString == null)) {
|
||||
if ("GET".equals(httpMethod) || (useDigestAuth && digestString == null)) {
|
||||
request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, new HttpMethod(httpMethod), httpRequestURL);
|
||||
request.headers().set("Host", cameraConfig.getIp() + ":" + port);
|
||||
request.headers().set("Connection", HttpHeaderValues.CLOSE);
|
||||
} else {
|
||||
} else if ("PUT".equals(httpMethod)) {
|
||||
request = putRequestWithBody;
|
||||
} else {
|
||||
request = postRequestWithBody;
|
||||
}
|
||||
|
||||
if (!basicAuth.isEmpty()) {
|
||||
@@ -630,6 +657,10 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
InstarHandler instarHandler = (InstarHandler) ch.pipeline().get(INSTAR_HANDLER);
|
||||
instarHandler.setURL(httpRequestURL);
|
||||
break;
|
||||
case REOLINK_THING:
|
||||
ReolinkHandler reolinkHandler = (ReolinkHandler) ch.pipeline().get(REOLINK_HANDLER);
|
||||
reolinkHandler.setURL(httpRequestURL);
|
||||
break;
|
||||
}
|
||||
ch.writeAndFlush(request);
|
||||
} else { // an error occured
|
||||
@@ -1025,6 +1056,12 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
setChannelState(CHANNEL_RECORDING_GIF, DecimalType.valueOf(new String("" + seconds)));
|
||||
}
|
||||
|
||||
private void getReolinkToken() {
|
||||
sendHttpPOST("/api.cgi?cmd=Login",
|
||||
"[{\"cmd\":\"Login\", \"param\":{ \"User\":{ \"Version\": \"0\", \"userName\":\""
|
||||
+ cameraConfig.getUser() + "\", \"password\":\"" + cameraConfig.getPassword() + "\"}}}]");
|
||||
}
|
||||
|
||||
public String returnValueFromString(String rawString, String searchedString) {
|
||||
String result = "";
|
||||
int index = rawString.indexOf(searchedString);
|
||||
@@ -1063,6 +1100,14 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeChannels(List<org.openhab.core.thing.Channel> removeChannels) {
|
||||
if (!removeChannels.isEmpty()) {
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
thingBuilder.withoutChannels(removeChannels);
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
@@ -1287,6 +1332,10 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
lowPriorityRequests = instarHandler.getLowPriorityRequests();
|
||||
}
|
||||
break;
|
||||
case REOLINK_THING:
|
||||
ReolinkHandler reolinkHandler = new ReolinkHandler(getHandle());
|
||||
reolinkHandler.handleCommand(channelUID, command);
|
||||
break;
|
||||
default:
|
||||
HttpOnlyHandler defaultHandler = new HttpOnlyHandler(getHandle());
|
||||
defaultHandler.handleCommand(channelUID, command);
|
||||
@@ -1538,6 +1587,21 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
sendHttpGET("/cgi-bin/eventManager.cgi?action=getEventIndexes&code=VideoMotion");
|
||||
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 (!onvifCamera.isConnected()) {
|
||||
onvifCamera.connect(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DAHUA_THING:
|
||||
if (!snapshotPolling) {
|
||||
checkCameraConnection();
|
||||
@@ -1663,6 +1727,30 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
+ getThing().getUID().getId()
|
||||
+ "/instar&-as_ssl=0&-as_mode=1&-as_activequery=1&-as_auth=0&-as_query1=0&-as_query2=0&-as_query3=0&-as_query4=0&-as_query5=0");
|
||||
break;
|
||||
case REOLINK_THING:
|
||||
if (cameraConfig.useToken) {
|
||||
authenticationJob = threadPool.scheduleWithFixedDelay(this::getReolinkToken, 0, 45,
|
||||
TimeUnit.MINUTES);
|
||||
} else {
|
||||
reolinkAuth = "&user=" + cameraConfig.getUser() + "&password=" + cameraConfig.getPassword();
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
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";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
// for poll times 9 seconds and above don't display a warning about the Image channel.
|
||||
if (9000 > cameraConfig.getPollTime() && cameraConfig.getUpdateImageWhen().contains("1")) {
|
||||
@@ -1681,11 +1769,23 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
cameraConfig.getUser(), cameraConfig.getPassword());
|
||||
onvifCamera.setSelectedMediaProfile(cameraConfig.getOnvifMediaProfile());
|
||||
// Only use ONVIF events if it is not an API camera.
|
||||
onvifCamera.connect(thing.getThingTypeUID().getId().equals(ONVIF_THING));
|
||||
onvifCamera.connect(supportsOnvifEvents());
|
||||
}
|
||||
cameraConnectionJob = threadPool.scheduleWithFixedDelay(this::pollingCameraConnection, 4, 8, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private boolean supportsOnvifEvents() {
|
||||
switch (thing.getThingTypeUID().getId()) {
|
||||
case ONVIF_THING:
|
||||
return true;
|
||||
case REOLINK_THING:
|
||||
if (cameraConfig.getNvrChannel() < 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void keepMjpegRunning() {
|
||||
CameraServlet localServlet = servlet;
|
||||
if (localServlet != null && !localServlet.openStreams.isEmpty()) {
|
||||
@@ -1708,17 +1808,22 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
Future<?> localFuture = pollCameraJob;
|
||||
if (localFuture != null) {
|
||||
localFuture.cancel(true);
|
||||
localFuture = null;
|
||||
pollCameraJob = null;
|
||||
}
|
||||
localFuture = authenticationJob;
|
||||
if (localFuture != null) {
|
||||
localFuture.cancel(true);
|
||||
authenticationJob = null;
|
||||
}
|
||||
localFuture = snapshotJob;
|
||||
if (localFuture != null) {
|
||||
localFuture.cancel(true);
|
||||
localFuture = null;
|
||||
snapshotJob = null;
|
||||
}
|
||||
localFuture = cameraConnectionJob;
|
||||
if (localFuture != null) {
|
||||
localFuture.cancel(true);
|
||||
localFuture = null;
|
||||
cameraConnectionJob = null;
|
||||
}
|
||||
Ffmpeg localFfmpeg = ffmpegHLS;
|
||||
if (localFfmpeg != null) {
|
||||
@@ -1762,7 +1867,7 @@ public class IpCameraHandler extends BaseThingHandler {
|
||||
CameraServlet localServlet = servlet;
|
||||
if (localServlet != null) {
|
||||
localServlet.dispose();
|
||||
localServlet = null;
|
||||
servlet = null;
|
||||
}
|
||||
threadPool.shutdown();
|
||||
// inform all group handlers that this camera has gone offline
|
||||
|
||||
@@ -316,13 +316,18 @@ public class OnvifConnection {
|
||||
} finally {
|
||||
connecting.unlock();
|
||||
}
|
||||
sendOnvifRequest(requestBuilder(RequestType.GetCapabilities, deviceXAddr));
|
||||
parseDateAndTime(message);
|
||||
logger.debug("Openhabs UTC dateTime is:{}", getUTCdateTime());
|
||||
} else if (message.contains("GetCapabilitiesResponse")) {// 2nd to be sent.
|
||||
parseXAddr(message);
|
||||
sendOnvifRequest(requestBuilder(RequestType.GetProfiles, mediaXAddr));
|
||||
} else if (message.contains("GetProfilesResponse")) {// 3rd to be sent.
|
||||
connecting.lock();
|
||||
try {
|
||||
isConnected = true;
|
||||
} finally {
|
||||
connecting.unlock();
|
||||
}
|
||||
parseProfiles(message);
|
||||
sendOnvifRequest(requestBuilder(RequestType.GetSnapshotUri, mediaXAddr));
|
||||
sendOnvifRequest(requestBuilder(RequestType.GetStreamUri, mediaXAddr));
|
||||
@@ -472,7 +477,21 @@ public class OnvifConnection {
|
||||
ptzXAddr = Helper.fetchXML(message, "<tt:PTZ", "tt:XAddr");
|
||||
if (ptzXAddr.isEmpty()) {
|
||||
ptzDevice = false;
|
||||
logger.trace("Camera must not support PTZ, it failed to give a <tt:PTZ><tt:XAddr>:{}", message);
|
||||
logger.debug("Camera has no ONVIF PTZ support.");
|
||||
List<org.openhab.core.thing.Channel> removeChannels = new ArrayList<>();
|
||||
org.openhab.core.thing.Channel channel = ipCameraHandler.getThing().getChannel(CHANNEL_PAN);
|
||||
if (channel != null) {
|
||||
removeChannels.add(channel);
|
||||
}
|
||||
channel = ipCameraHandler.getThing().getChannel(CHANNEL_TILT);
|
||||
if (channel != null) {
|
||||
removeChannels.add(channel);
|
||||
}
|
||||
channel = ipCameraHandler.getThing().getChannel(CHANNEL_ZOOM);
|
||||
if (channel != null) {
|
||||
removeChannels.add(channel);
|
||||
}
|
||||
ipCameraHandler.removeChannels(removeChannels);
|
||||
} else {
|
||||
logger.debug("ptzXAddr:{}", ptzXAddr);
|
||||
}
|
||||
@@ -601,11 +620,13 @@ public class OnvifConnection {
|
||||
|
||||
public void eventRecieved(String eventMessage) {
|
||||
String topic = Helper.fetchXML(eventMessage, "Topic", "tns1:");
|
||||
if (topic.isEmpty()) {
|
||||
sendOnvifRequest(requestBuilder(RequestType.Renew, subscriptionXAddr));
|
||||
return;
|
||||
}
|
||||
String dataName = Helper.fetchXML(eventMessage, "tt:Data", "Name=\"");
|
||||
String dataValue = Helper.fetchXML(eventMessage, "tt:Data", "Value=\"");
|
||||
if (!topic.isEmpty()) {
|
||||
logger.debug("Onvif Event Topic:{}, Data:{}, Value:{}", topic, dataName, dataValue);
|
||||
}
|
||||
logger.debug("Onvif Event Topic:{}, Data:{}, Value:{}", topic, dataName, dataValue);
|
||||
switch (topic) {
|
||||
case "RuleEngine/CellMotionDetector/Motion":
|
||||
if ("true".equals(dataValue)) {
|
||||
@@ -692,7 +713,43 @@ public class OnvifConnection {
|
||||
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);
|
||||
}
|
||||
sendOnvifRequest(requestBuilder(RequestType.Renew, subscriptionXAddr));
|
||||
}
|
||||
@@ -856,6 +913,7 @@ public class OnvifConnection {
|
||||
threadPool = Executors.newScheduledThreadPool(2);
|
||||
sendOnvifRequest(requestBuilder(RequestType.GetSystemDateAndTime, deviceXAddr));
|
||||
usingEvents = useEvents;
|
||||
sendOnvifRequest(requestBuilder(RequestType.GetCapabilities, deviceXAddr));
|
||||
}
|
||||
} finally {
|
||||
connecting.unlock();
|
||||
|
||||
@@ -153,6 +153,8 @@ public class OnvifDiscovery {
|
||||
return "dahua";
|
||||
} else if (response.toLowerCase().contains("dh-sd")) {
|
||||
return "dahua";
|
||||
} else if (response.toLowerCase().contains("reolink")) {
|
||||
return "reolink";
|
||||
}
|
||||
return "onvif";
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ thing-type.ipcamera.instar.label = Instar Camera with API
|
||||
thing-type.ipcamera.instar.description = Use for all current INSTAR HD Cameras, as they support an API as well as ONVIF.
|
||||
thing-type.ipcamera.onvif.label = ONVIF IP Camera
|
||||
thing-type.ipcamera.onvif.description = Use when the binding does not list your brand of ONVIF camera.
|
||||
thing-type.ipcamera.reolink.label = Reolink Camera with API
|
||||
thing-type.ipcamera.reolink.description = Use for all Reolink cameras, as they support an API as well as ONVIF.
|
||||
|
||||
# thing types config
|
||||
|
||||
@@ -548,6 +550,72 @@ thing-type.config.ipcamera.onvif.updateImageWhen.option.5 = During Audio Alarm (
|
||||
thing-type.config.ipcamera.onvif.updateImageWhen.option.45 = During Motion and Audio Alarms (45)
|
||||
thing-type.config.ipcamera.onvif.username.label = Username
|
||||
thing-type.config.ipcamera.onvif.username.description = Enter the User name used to connect to your camera. Leave blank if your camera does not use login details.
|
||||
thing-type.config.ipcamera.reolink.alarmInputUrl.label = Alarm Input URL
|
||||
thing-type.config.ipcamera.reolink.alarmInputUrl.description = Leave blank to use the ffmpegInput as the source for detecting motion with FFmpeg, or enter any HTTP or RTSP URL. TIP: Using a low res source can save CPU usage.
|
||||
thing-type.config.ipcamera.reolink.ffmpegInput.label = FFmpeg Input
|
||||
thing-type.config.ipcamera.reolink.ffmpegInput.description = Leave this blank to use the auto detected RTSP address, or enter a URL for any type of stream that FFmpeg can use as an input.
|
||||
thing-type.config.ipcamera.reolink.ffmpegInputOptions.label = FFmpeg Input Options
|
||||
thing-type.config.ipcamera.reolink.ffmpegInputOptions.description = This gives you direct access to specify FFmpeg options before the -i.
|
||||
thing-type.config.ipcamera.reolink.ffmpegLocation.label = FFmpeg Install Location
|
||||
thing-type.config.ipcamera.reolink.ffmpegLocation.description = The full path including the filename for where you have installed FFmpeg. For windows use this format, c:\ffmpeg\bin\ffmpeg.exe
|
||||
thing-type.config.ipcamera.reolink.ffmpegOutput.label = FFmpeg Output Folder
|
||||
thing-type.config.ipcamera.reolink.ffmpegOutput.description = Leave this blank and the binding will use the openHAB userdata folder. Alternatively, a unique path for each camera that ends with a slash and has write permissions can be entered.
|
||||
thing-type.config.ipcamera.reolink.gifOutOptions.label = GIF Out Options
|
||||
thing-type.config.ipcamera.reolink.gifOutOptions.description = This gives you direct access to specify your own FFmpeg options to be used for animated GIF files.
|
||||
thing-type.config.ipcamera.reolink.gifPreroll.label = GIF Preroll
|
||||
thing-type.config.ipcamera.reolink.gifPreroll.description = Store this many snapshots from BEFORE you trigger a GIF creation.
|
||||
thing-type.config.ipcamera.reolink.group.FFmpeg Setup.label = FFmpeg Settings
|
||||
thing-type.config.ipcamera.reolink.group.FFmpeg Setup.description = Settings that setup or effect the video stream.
|
||||
thing-type.config.ipcamera.reolink.group.Image ch Settings.label = Image channel settings
|
||||
thing-type.config.ipcamera.reolink.group.Image ch Settings.description = Settings for the image channel features which is not recommended to be used. See readme for more info.
|
||||
thing-type.config.ipcamera.reolink.group.Settings.label = Main Settings
|
||||
thing-type.config.ipcamera.reolink.group.Settings.description = Settings required to connect to the camera.
|
||||
thing-type.config.ipcamera.reolink.hlsOutOptions.label = HLS Out Options
|
||||
thing-type.config.ipcamera.reolink.hlsOutOptions.description = This gives you direct access to specify your own FFmpeg options to be used.
|
||||
thing-type.config.ipcamera.reolink.ipAddress.label = IP Address
|
||||
thing-type.config.ipcamera.reolink.ipAddress.description = Use this format 192.168.1.2 and do not include the port number.
|
||||
thing-type.config.ipcamera.reolink.ipWhitelist.label = IP Whitelist
|
||||
thing-type.config.ipcamera.reolink.ipWhitelist.description = Enter any IP's inside (brackets) that you wish to allow to access the video stream. 'DISABLE' will allow all devices on your network unrestricted access.
|
||||
thing-type.config.ipcamera.reolink.mjpegOptions.label = MJPEG Options
|
||||
thing-type.config.ipcamera.reolink.mjpegOptions.description = This gives you direct access to specify your own FFmpeg options to be used for MJPEG streams.
|
||||
thing-type.config.ipcamera.reolink.mjpegUrl.label = MJPEG URL
|
||||
thing-type.config.ipcamera.reolink.mjpegUrl.description = Leave this blank to use the auto detected URL, or enter a full HTTP address to where a MJPEG stream can be watched if entered into any browser.
|
||||
thing-type.config.ipcamera.reolink.motionOptions.label = Motion Options
|
||||
thing-type.config.ipcamera.reolink.motionOptions.description = This gives you direct access to specify your own FFmpeg options to be used for detecting motion.
|
||||
thing-type.config.ipcamera.reolink.mp4OutOptions.label = MP4 Out Options
|
||||
thing-type.config.ipcamera.reolink.mp4OutOptions.description = This gives you direct access to specify your own FFmpeg options to be used for recording MP4 files.
|
||||
thing-type.config.ipcamera.reolink.nvrChannel.label = NVR Input Channel
|
||||
thing-type.config.ipcamera.reolink.nvrChannel.description = Set this to 0 if it is a stand alone camera, or to the input channel number of your NVR that the camera is connected to.
|
||||
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.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
|
||||
thing-type.config.ipcamera.reolink.pollTime.description = Most features are made on demand and not polled, but some features require a regular snapshot to work. Default is "1000" which is 1 second.
|
||||
thing-type.config.ipcamera.reolink.port.label = Port for HTTP
|
||||
thing-type.config.ipcamera.reolink.port.description = This port will be used for HTTP calls for fetching the snapshot and alarm states.
|
||||
thing-type.config.ipcamera.reolink.ptzContinuous.label = Use Continuous PTZ
|
||||
thing-type.config.ipcamera.reolink.ptzContinuous.description = Select if you want Relative (false) or Continuous (true) movements.
|
||||
thing-type.config.ipcamera.reolink.snapshotOptions.label = Snapshot Options
|
||||
thing-type.config.ipcamera.reolink.snapshotOptions.description = Specify your own FFmpeg options to be used when creating snapshots from RTSP.
|
||||
thing-type.config.ipcamera.reolink.snapshotUrl.label = Snapshot URL
|
||||
thing-type.config.ipcamera.reolink.snapshotUrl.description = Leave blank to use the autodetected URL for snapshots, or enter a HTTP URL to where a snapshot can be seen if entered into any browser.
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.label = Update Image Channel When:
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.description = The Image channel can be set to update in a number of ways. Recommend you set this to never updates as per the readme.
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.option.0 = Image channel never updates (0)
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.option.1 = Image channel follows pollImage (1)
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.option.2 = Start Motion Alarm (2)
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.option.3 = Start Audio Alarm (3)
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.option.23 = Start of Motion and Audio Alarms (23)
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.option.4 = During Motion Alarm (4)
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.option.5 = During Audio Alarm (5)
|
||||
thing-type.config.ipcamera.reolink.updateImageWhen.option.45 = During Motion and Audio Alarms (45)
|
||||
thing-type.config.ipcamera.reolink.useToken.label = Use API Token
|
||||
thing-type.config.ipcamera.reolink.useToken.description = True if you want to use a Token, or false to use the user/password in each URL sent.
|
||||
thing-type.config.ipcamera.reolink.username.label = Username
|
||||
thing-type.config.ipcamera.reolink.username.description = Enter the User name used to connect to your camera. Leave blank if your camera does not use login details.
|
||||
|
||||
# channel types
|
||||
|
||||
@@ -555,6 +623,8 @@ channel-type.ipcamera.activateAlarmOutput.label = Alarm Output 1 ON/OFF
|
||||
channel-type.ipcamera.activateAlarmOutput.description = You can use the cameras output to trigger a device like a burglar alarm.
|
||||
channel-type.ipcamera.activateAlarmOutput2.label = Alarm Output 2 ON/OFF
|
||||
channel-type.ipcamera.activateAlarmOutput2.description = You can use the cameras output 2 to trigger a device like a burglar alarm.
|
||||
channel-type.ipcamera.animalAlarm.label = Animal Alarm
|
||||
channel-type.ipcamera.animalAlarm.description = An animal has triggered the object detection.
|
||||
channel-type.ipcamera.audioAlarm.label = Audio Alarm
|
||||
channel-type.ipcamera.audioAlarm.description = Audio has triggered an Alarm.
|
||||
channel-type.ipcamera.autoLED.label = Auto LED
|
||||
@@ -569,6 +639,8 @@ channel-type.ipcamera.enableAudioAlarm.label = Enable Audio Alarm
|
||||
channel-type.ipcamera.enableAudioAlarm.description = By using this feature you can stop the camera from sending e-mails when you are having a party.
|
||||
channel-type.ipcamera.enableExternalAlarmInput.label = Enable Alarm Input 1
|
||||
channel-type.ipcamera.enableExternalAlarmInput.description = Turn the External Alarm Input feature on and off.
|
||||
channel-type.ipcamera.enableFTP.label = Enable FTP
|
||||
channel-type.ipcamera.enableFTP.description = Turn the FTP features of the camera on and off
|
||||
channel-type.ipcamera.enableFieldDetectionAlarm.label = Enable Field Alarm
|
||||
channel-type.ipcamera.enableFieldDetectionAlarm.description = By using this feature you can stop the camera from sending e-mails when you are actually home.
|
||||
channel-type.ipcamera.enableLED.label = LED Controls
|
||||
@@ -581,6 +653,8 @@ channel-type.ipcamera.enablePirAlarm.label = Enable PIR Alarm
|
||||
channel-type.ipcamera.enablePirAlarm.description = Enable/Disable the PIR Alarm.
|
||||
channel-type.ipcamera.enablePrivacyMode.label = Enable Privacy Mode
|
||||
channel-type.ipcamera.enablePrivacyMode.description = Turn the Privacy Mode on and off.
|
||||
channel-type.ipcamera.enableRecordings.label = Enable Recordings
|
||||
channel-type.ipcamera.enableRecordings.description = Enable/Disable the cameras internal recordings
|
||||
channel-type.ipcamera.externalAlarmInput.label = Alarm Input 1
|
||||
channel-type.ipcamera.externalAlarmInput.description = Some cameras have alarm input wires which can be used to connect to door bells or external PIR sensors.
|
||||
channel-type.ipcamera.externalAlarmInput2.label = Alarm Input 2
|
||||
|
||||
@@ -351,6 +351,9 @@
|
||||
<channel id="storageAlarm" typeId="storageAlarm"/>
|
||||
<channel id="sceneChangeAlarm" typeId="sceneChangeAlarm"/>
|
||||
<channel id="tooBrightAlarm" typeId="tooBrightAlarm"/>
|
||||
<channel id="humanAlarm" typeId="humanAlarm"/>
|
||||
<channel id="animalAlarm" typeId="animalAlarm"/>
|
||||
<channel id="carAlarm" typeId="carAlarm"/>
|
||||
<channel id="tooBlurryAlarm" typeId="tooBlurryAlarm"/>
|
||||
<channel id="pan" typeId="pan"/>
|
||||
<channel id="tilt" typeId="tilt"/>
|
||||
@@ -2257,7 +2260,297 @@
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<thing-type id="reolink">
|
||||
<label>Reolink Camera with API</label>
|
||||
<description>Use for all Reolink cameras, as they support an API as well as ONVIF.</description>
|
||||
<channels>
|
||||
<channel id="startStream" typeId="startStream"/>
|
||||
<channel id="pollImage" typeId="pollImage"/>
|
||||
<channel id="image" typeId="image"/>
|
||||
<channel id="recordingGif" typeId="recordingGif"/>
|
||||
<channel id="gifHistory" typeId="gifHistory"/>
|
||||
<channel id="gifHistoryLength" typeId="gifHistoryLength"/>
|
||||
<channel id="recordingMp4" typeId="recordingMp4"/>
|
||||
<channel id="mp4History" typeId="mp4History"/>
|
||||
<channel id="mp4HistoryLength" typeId="mp4HistoryLength"/>
|
||||
<channel id="lastMotionType" typeId="lastMotionType"/>
|
||||
<channel id="lastEventData" typeId="lastEventData"/>
|
||||
<channel id="ffmpegMotionControl" typeId="ffmpegMotionControl"/>
|
||||
<channel id="ffmpegMotionAlarm" typeId="ffmpegMotionAlarm"/>
|
||||
<channel id="enableMotionAlarm" typeId="enableMotionAlarm"/>
|
||||
<channel id="motionAlarm" typeId="motionAlarm"/>
|
||||
<channel id="cellMotionAlarm" typeId="cellMotionAlarm"/>
|
||||
<channel id="externalMotion" typeId="externalMotion"/>
|
||||
<channel id="enableAudioAlarm" typeId="enableAudioAlarm"/>
|
||||
<channel id="thresholdAudioAlarm" typeId="thresholdAudioAlarm"/>
|
||||
<channel id="audioAlarm" typeId="audioAlarm"/>
|
||||
<channel id="pan" typeId="pan"/>
|
||||
<channel id="tilt" typeId="tilt"/>
|
||||
<channel id="zoom" typeId="zoom"/>
|
||||
<channel id="gotoPreset" typeId="gotoPreset"/>
|
||||
<channel id="mjpegUrl" typeId="mjpegUrl"/>
|
||||
<channel id="rtspUrl" typeId="rtspUrl"/>
|
||||
<channel id="imageUrl" typeId="imageUrl"/>
|
||||
<channel id="hlsUrl" typeId="hlsUrl"/>
|
||||
<channel id="carAlarm" typeId="carAlarm"/>
|
||||
<channel id="humanAlarm" typeId="humanAlarm"/>
|
||||
<channel id="animalAlarm" typeId="animalAlarm"/>
|
||||
<channel id="faceDetected" typeId="faceDetected"/>
|
||||
<channel id="autoLED" typeId="autoLED"/>
|
||||
<channel id="enableLED" typeId="enableLED"/>
|
||||
<channel id="textOverlay" typeId="textOverlay"/>
|
||||
<channel id="activateAlarmOutput" typeId="activateAlarmOutput"/>
|
||||
<channel id="doorBell" typeId="doorBell"/>
|
||||
<channel id="enableRecordings" typeId="enableRecordings"/>
|
||||
<channel id="enableFTP" typeId="enableFTP"/>
|
||||
</channels>
|
||||
<config-description>
|
||||
|
||||
<parameter-group name="Settings">
|
||||
<label>Main Settings</label>
|
||||
<description>Settings required to connect to the camera.</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter-group>
|
||||
|
||||
<parameter-group name="FFmpeg Setup">
|
||||
<label>FFmpeg Settings</label>
|
||||
<description>Settings that setup or effect the video stream.</description>
|
||||
<advanced>false</advanced>
|
||||
</parameter-group>
|
||||
|
||||
<parameter-group name="Image ch Settings">
|
||||
<label>Image channel settings</label>
|
||||
<description>Settings for the image channel features which is not recommended to be used. See readme for more info.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter-group>
|
||||
|
||||
<parameter name="mjpegUrl" type="text" required="false" groupName="Settings">
|
||||
<context>url</context>
|
||||
<label>MJPEG URL</label>
|
||||
<description>Leave this blank to use the auto detected URL, or enter a full HTTP address to where a MJPEG stream can
|
||||
be watched if entered into any browser.
|
||||
</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="ffmpegInput" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<context>url</context>
|
||||
<label>FFmpeg Input</label>
|
||||
<description>Leave this blank to use the auto detected RTSP address, or enter a URL for any type of stream that
|
||||
FFmpeg can use as an input.
|
||||
</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="ffmpegInputOptions" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<label>FFmpeg Input Options</label>
|
||||
<description>This gives you direct access to specify FFmpeg options before the -i.
|
||||
</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="ffmpegLocation" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<label>FFmpeg Install Location</label>
|
||||
<description>The full path including the filename for where you have installed FFmpeg. For windows use this format,
|
||||
c:\ffmpeg\bin\ffmpeg.exe
|
||||
</description>
|
||||
<default>/usr/bin/ffmpeg</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="ffmpegOutput" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<label>FFmpeg Output Folder</label>
|
||||
<description>Leave this blank and the binding will use the openHAB userdata folder. Alternatively, a unique path for
|
||||
each camera that ends with a slash and has write permissions can be entered.
|
||||
</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="hlsOutOptions" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<label>HLS Out Options</label>
|
||||
<description>This gives you direct access to specify your own FFmpeg options to be used.
|
||||
</description>
|
||||
<default>-strict -2 -f lavfi -i aevalsrc=0 -acodec aac -vcodec copy -hls_flags delete_segments -hls_time 2
|
||||
-hls_list_size 4</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="gifOutOptions" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<label>GIF Out Options</label>
|
||||
<description>This gives you direct access to specify your own FFmpeg options to be used for animated GIF files.
|
||||
</description>
|
||||
<default>-r 2 -filter_complex
|
||||
scale=-2:360:flags=lanczos,setpts=0.5*PTS,split[o1][o2];[o1]palettegen[p];[o2]fifo[o3];[o3][p]paletteuse</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="mp4OutOptions" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<label>MP4 Out Options</label>
|
||||
<description>This gives you direct access to specify your own FFmpeg options to be used for recording MP4 files.
|
||||
</description>
|
||||
<default>-c:v copy -c:a copy</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="mjpegOptions" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<label>MJPEG Options</label>
|
||||
<description>This gives you direct access to specify your own FFmpeg options to be used for MJPEG streams.
|
||||
</description>
|
||||
<default>-q:v 5 -r 2 -vf scale=640:-2 -update 1</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="snapshotOptions" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<label>Snapshot Options</label>
|
||||
<description>Specify your own FFmpeg options to be used when creating snapshots from RTSP.
|
||||
</description>
|
||||
<default>-an -vsync vfr -q:v 2 -update 1</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="alarmInputUrl" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<context>url</context>
|
||||
<label>Alarm Input URL</label>
|
||||
<description>Leave blank to use the ffmpegInput as the source for detecting motion with FFmpeg, or enter any HTTP
|
||||
or
|
||||
RTSP URL. TIP: Using a low res source can save CPU usage.
|
||||
</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="motionOptions" type="text" required="false" groupName="FFmpeg Setup">
|
||||
<label>Motion Options</label>
|
||||
<description>This gives you direct access to specify your own FFmpeg options to be used for detecting motion.
|
||||
</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="gifPreroll" type="integer" required="true" min="0" max="30" groupName="Settings">
|
||||
<label>GIF Preroll</label>
|
||||
<description>Store this many snapshots from BEFORE you trigger a GIF creation.
|
||||
</description>
|
||||
<default>0</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="updateImageWhen" type="text" groupName="Image ch Settings" multiple="false">
|
||||
<label>Update Image Channel When:</label>
|
||||
<description>The Image channel can be set to update in a number of ways. Recommend you set this to never updates as
|
||||
per the readme.
|
||||
</description>
|
||||
<default>0</default>
|
||||
<advanced>true</advanced>
|
||||
<options>
|
||||
<option value="0">Image channel never updates (0)</option>
|
||||
<option value="1">Image channel follows pollImage (1)</option>
|
||||
<option value="2">Start Motion Alarm (2)</option>
|
||||
<option value="3">Start Audio Alarm (3)</option>
|
||||
<option value="23">Start of Motion and Audio Alarms (23)</option>
|
||||
<option value="4">During Motion Alarm (4)</option>
|
||||
<option value="5">During Audio Alarm (5)</option>
|
||||
<option value="45">During Motion and Audio Alarms (45)</option>
|
||||
</options>
|
||||
</parameter>
|
||||
|
||||
<parameter name="ipAddress" type="text" required="true" groupName="Settings">
|
||||
<context>network-address</context>
|
||||
<label>IP Address</label>
|
||||
<description>Use this format 192.168.1.2 and do not include the port number.
|
||||
</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="nvrChannel" type="integer" required="true" min="0" max="255" groupName="Settings">
|
||||
<label>NVR Input Channel</label>
|
||||
<description>Set this to 0 if it is a stand alone camera, or to the input channel number of your NVR that the
|
||||
camera
|
||||
is connected to.
|
||||
</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="port" type="integer" required="true" min="1" max="65535" groupName="Settings">
|
||||
<label>Port for HTTP</label>
|
||||
<description>This port will be used for HTTP calls for fetching the snapshot and alarm states.
|
||||
</description>
|
||||
<default>80</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="snapshotUrl" type="text" required="false" groupName="Settings">
|
||||
<context>url</context>
|
||||
<label>Snapshot URL</label>
|
||||
<description>Leave blank to use the autodetected URL for snapshots, or enter a HTTP URL to where a snapshot can be
|
||||
seen if entered into any browser.
|
||||
</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="onvifPort" type="integer" required="true" groupName="Settings">
|
||||
<label>ONVIF Port</label>
|
||||
<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.
|
||||
</description>
|
||||
<default>8000</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="username" type="text" required="false" groupName="Settings">
|
||||
<label>Username</label>
|
||||
<description>Enter the User name used to connect to your camera. Leave blank if your camera does not use login
|
||||
details.
|
||||
</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="password" type="text" required="false" groupName="Settings">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>Enter the password for your camera. Leave blank if your camera does not use one.
|
||||
</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="useToken" type="boolean" groupName="Settings">
|
||||
<label>Use API Token</label>
|
||||
<description>True if you want to use a Token, or false to use the user/password in each URL sent.
|
||||
</description>
|
||||
<default>true</default>
|
||||
</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
|
||||
allow all devices on your network unrestricted access.
|
||||
</description>
|
||||
<default>DISABLE</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="ptzContinuous" type="boolean" groupName="Settings">
|
||||
<label>Use Continuous PTZ</label>
|
||||
<description>Select if you want Relative (false) or Continuous (true) movements.
|
||||
</description>
|
||||
<default>true</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="onvifMediaProfile" type="integer" required="true" min="0" max="5" groupName="Settings">
|
||||
<label>ONVIF Media Profile</label>
|
||||
<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.
|
||||
</description>
|
||||
<default>0</default>
|
||||
</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.
|
||||
Default is "1000" which is 1 second.
|
||||
</description>
|
||||
<default>1000</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<channel-type id="image" advanced="true">
|
||||
<item-type>Image</item-type>
|
||||
<label>Image</label>
|
||||
@@ -2661,6 +2954,18 @@
|
||||
<description>Turn the Privacy Mode on and off.</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="enableRecordings" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Enable Recordings</label>
|
||||
<description>Enable/Disable the cameras internal recordings</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="enableFTP" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Enable FTP</label>
|
||||
<description>Turn the FTP features of the camera on and off</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="autoLED" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Auto LED</label>
|
||||
|
||||
Reference in New Issue
Block a user