]> git.basschouten.com Git - openhab-addons.git/blob
fc48f939588602bd305cf49c196157a0af0492b0
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.ipcamera.internal;
14
15 import static org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.*;
16
17 import java.nio.charset.StandardCharsets;
18 import java.util.ArrayList;
19 import java.util.List;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
24 import org.openhab.core.library.types.OnOffType;
25 import org.openhab.core.library.types.StringType;
26 import org.openhab.core.thing.ChannelUID;
27 import org.openhab.core.thing.binding.ThingHandler;
28 import org.openhab.core.types.Command;
29 import org.openhab.core.types.RefreshType;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 import io.netty.buffer.ByteBuf;
34 import io.netty.buffer.Unpooled;
35 import io.netty.channel.ChannelDuplexHandler;
36 import io.netty.channel.ChannelHandlerContext;
37 import io.netty.handler.codec.http.DefaultFullHttpRequest;
38 import io.netty.handler.codec.http.FullHttpRequest;
39 import io.netty.handler.codec.http.HttpHeaderNames;
40 import io.netty.handler.codec.http.HttpHeaderValues;
41 import io.netty.handler.codec.http.HttpMethod;
42 import io.netty.handler.codec.http.HttpVersion;
43 import io.netty.util.ReferenceCountUtil;
44
45 /**
46  * The {@link HikvisionHandler} is responsible for handling commands, which are
47  * sent to one of the channels.
48  *
49  * @author Matthew Skinner - Initial contribution
50  */
51
52 @NonNullByDefault
53 public class HikvisionHandler extends ChannelDuplexHandler {
54     private final Logger logger = LoggerFactory.getLogger(getClass());
55     private IpCameraHandler ipCameraHandler;
56     private int nvrChannel;
57     private int lineCount, vmdCount, leftCount, takenCount, faceCount, pirCount, fieldCount;
58     private String requestUrl = "";
59
60     public HikvisionHandler(ThingHandler handler, int nvrChannel) {
61         ipCameraHandler = (IpCameraHandler) handler;
62         this.nvrChannel = nvrChannel;
63     }
64
65     private void processEvent(String content) {
66         // some cameras use <dynChannelID> or <channelID> and NVRs use channel 0 to say all channels
67         if (content.contains("hannelID>" + nvrChannel) || content.contains("<channelID>0</channelID>")) {
68             final int debounce = 3;
69             String eventType = Helper.fetchXML(content, "", "<eventType>");
70             ipCameraHandler.setChannelState(CHANNEL_LAST_EVENT_DATA, new StringType(content));
71             switch (eventType) {
72                 case "videoloss":
73                     if (content.contains("<eventState>inactive</eventState>")) {
74                         if (vmdCount > 1) {
75                             vmdCount = 1;
76                         }
77                         countDown();
78                         countDown();
79                     }
80                     break;
81                 case "PIR":
82                     ipCameraHandler.motionDetected(CHANNEL_PIR_ALARM);
83                     pirCount = debounce;
84                     break;
85                 case "attendedBaggage":
86                     ipCameraHandler.setChannelState(CHANNEL_ITEM_TAKEN, OnOffType.ON);
87                     takenCount = debounce;
88                     break;
89                 case "unattendedBaggage":
90                     ipCameraHandler.setChannelState(CHANNEL_ITEM_LEFT, OnOffType.ON);
91                     leftCount = debounce;
92                     break;
93                 case "facedetection":
94                     ipCameraHandler.setChannelState(CHANNEL_FACE_DETECTED, OnOffType.ON);
95                     faceCount = debounce;
96                     break;
97                 case "VMD":
98                     ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
99                     vmdCount = debounce;
100                     break;
101                 case "fielddetection":
102                     ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM);
103                     fieldCount = debounce;
104                     break;
105                 case "linedetection":
106                     ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM);
107                     lineCount = debounce;
108                     break;
109                 default:
110                     logger.debug("Unrecognised Hikvision eventType={}", eventType);
111             }
112         }
113         countDown();
114     }
115
116     public void setURL(String url) {
117         requestUrl = url;
118     }
119
120     // This handles the incoming http replies back from the camera.
121     @Override
122     public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception {
123         if (msg == null || ctx == null) {
124             return;
125         }
126         try {
127             String content = msg.toString();
128             logger.trace("HTTP Result from {} contains \t:{}:", requestUrl, content);
129             switch (requestUrl) {
130                 case "/ISAPI/Event/notification/alertStream":
131                     int startIndex = content.indexOf("<");// skip to start of XML content
132                     if (startIndex != -1) {
133                         String eventData = content.substring(startIndex, content.length());
134                         processEvent(eventData);
135                     }
136                     return;
137                 case "/ISAPI/System/IO/capabilities": // Used to check if the camera supports IO
138                     List<org.openhab.core.thing.Channel> removeChannels = new ArrayList<>();
139                     org.openhab.core.thing.Channel channel;
140                     if (content.contains("<IOOutputPortNums>0<") || !content.contains("<IOOutputPortNums>")) {
141                         logger.debug("Camera does not support IO outputs.");
142                         channel = ipCameraHandler.getThing().getChannel(CHANNEL_ACTIVATE_ALARM_OUTPUT);
143                         if (channel != null) {
144                             removeChannels.add(channel);
145                         }
146                         channel = ipCameraHandler.getThing().getChannel(CHANNEL_ACTIVATE_ALARM_OUTPUT2);
147                         if (channel != null) {
148                             removeChannels.add(channel);
149                         }
150                     } else if (content.contains("<IOOutputPortNums>1<")) {
151                         channel = ipCameraHandler.getThing().getChannel(CHANNEL_ACTIVATE_ALARM_OUTPUT2);
152                         if (channel != null) {
153                             removeChannels.add(channel);
154                         }
155                     }
156                     ipCameraHandler.lowPriorityRequests.clear(); // no longer need to check if the IO is supported.
157                     if (content.contains("<IOInputPortNums>0<") || !content.contains("<IOInputPortNums>")) {
158                         logger.debug("Camera does not support IO inputs.");
159                         channel = ipCameraHandler.getThing().getChannel(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT);
160                         if (channel != null) {
161                             removeChannels.add(channel);
162                         }
163                         channel = ipCameraHandler.getThing().getChannel(CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT);
164                         if (channel != null) {
165                             removeChannels.add(channel);
166                         }
167                         channel = ipCameraHandler.getThing().getChannel(CHANNEL_EXTERNAL_ALARM_INPUT);
168                         if (channel != null) {
169                             removeChannels.add(channel);
170                         }
171                         channel = ipCameraHandler.getThing().getChannel(CHANNEL_EXTERNAL_ALARM_INPUT2);
172                         if (channel != null) {
173                             removeChannels.add(channel);
174                         }
175                     } else if (content.contains("<IOInputPortNums>1<")) {
176                         channel = ipCameraHandler.getThing().getChannel(CHANNEL_EXTERNAL_ALARM_INPUT2);
177                         if (channel != null) {
178                             removeChannels.add(channel);
179                         }
180                         // start checking the input IO status
181                         ipCameraHandler.lowPriorityRequests.set(0,
182                                 "/ISAPI/System/IO/inputs/" + ipCameraHandler.cameraConfig.getNvrChannel() + "/status");
183                     } else {
184                         // start checking the input IO status
185                         ipCameraHandler.lowPriorityRequests.set(0,
186                                 "/ISAPI/System/IO/inputs/" + ipCameraHandler.cameraConfig.getNvrChannel() + "/status");
187                     }
188                     ipCameraHandler.removeChannels(removeChannels);
189                     return;
190             }
191             String replyElement = Helper.fetchXML(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<");
192             switch (replyElement) {
193                 case "MotionDetection version=":
194                     ipCameraHandler.storeHttpReply(
195                             "/ISAPI/System/Video/inputs/channels/" + nvrChannel + "01/motionDetection", content);
196                     if (content.contains("<enabled>true</enabled>")) {
197                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.ON);
198                     } else if (content.contains("<enabled>false</enabled>")) {
199                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.OFF);
200                     }
201                     break;
202                 case "IOInputPort version=":
203                     ipCameraHandler.storeHttpReply("/ISAPI/System/IO/inputs/" + nvrChannel, content);
204                     if (content.contains("<enabled>true</enabled>")) {
205                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT, OnOffType.ON);
206                     } else if (content.contains("<enabled>false</enabled>")) {
207                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
208                     }
209                     if (content.contains("<triggering>low</triggering>")) {
210                         ipCameraHandler.setChannelState(CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
211                     } else if (content.contains("<triggering>high</triggering>")) {
212                         ipCameraHandler.setChannelState(CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT, OnOffType.ON);
213                     }
214                     break;
215                 case "LineDetection":
216                     ipCameraHandler.storeHttpReply("/ISAPI/Smart/LineDetection/" + nvrChannel + "01", content);
217                     if (content.contains("<enabled>true</enabled>")) {
218                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.ON);
219                     } else if (content.contains("<enabled>false</enabled>")) {
220                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.OFF);
221                     }
222                     break;
223                 case "TextOverlay version=":
224                     ipCameraHandler.storeHttpReply(
225                             "/ISAPI/System/Video/inputs/channels/" + nvrChannel + "/overlays/text/1", content);
226                     String text = Helper.fetchXML(content, "<enabled>true</enabled>", "<displayText>");
227                     ipCameraHandler.setChannelState(CHANNEL_TEXT_OVERLAY, StringType.valueOf(text));
228                     break;
229                 case "AudioDetection version=":
230                     ipCameraHandler.storeHttpReply("/ISAPI/Smart/AudioDetection/channels/" + nvrChannel + "01",
231                             content);
232                     if (content.contains("<enabled>true</enabled>")) {
233                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.ON);
234                     } else if (content.contains("<enabled>false</enabled>")) {
235                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.OFF);
236                     }
237                     break;
238                 case "IOPortStatus version=":
239                     if (content.contains("<ioState>active</ioState>")) {
240                         ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.ON);
241                     } else if (content.contains("<ioState>inactive</ioState>")) {
242                         ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
243                     }
244                     break;
245                 case "FieldDetection version=":
246                     ipCameraHandler.storeHttpReply("/ISAPI/Smart/FieldDetection/" + nvrChannel + "01", content);
247                     if (content.contains("<enabled>true</enabled>")) {
248                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_FIELD_DETECTION_ALARM, OnOffType.ON);
249                     } else if (content.contains("<enabled>false</enabled>")) {
250                         ipCameraHandler.setChannelState(CHANNEL_ENABLE_FIELD_DETECTION_ALARM, OnOffType.OFF);
251                     }
252                     break;
253                 case "ResponseStatus version=":
254                     ////////////////// External Alarm Input ///////////////
255                     if (content
256                             .contains("<requestURL>/ISAPI/System/IO/inputs/" + nvrChannel + "/status</requestURL>")) {
257                         // Stops checking the external alarm if camera does not have feature.
258                         if (content.contains("<statusString>Invalid Operation</statusString>")) {
259                             ipCameraHandler.lowPriorityRequests.remove(0);
260                             ipCameraHandler.logger.debug(
261                                     "Stopping checks for alarm inputs as camera appears to be missing this feature.");
262                         }
263                     }
264                     break;
265             }
266
267         } finally {
268             ReferenceCountUtil.release(msg);
269         }
270     }
271
272     // This does debouncing of the alarms
273     void countDown() {
274         if (lineCount > 1) {
275             lineCount--;
276         } else if (lineCount == 1) {
277             ipCameraHandler.setChannelState(CHANNEL_LINE_CROSSING_ALARM, OnOffType.OFF);
278             lineCount--;
279         }
280         if (vmdCount > 1) {
281             vmdCount--;
282         } else if (vmdCount == 1) {
283             ipCameraHandler.setChannelState(CHANNEL_MOTION_ALARM, OnOffType.OFF);
284             vmdCount--;
285         }
286         if (leftCount > 1) {
287             leftCount--;
288         } else if (leftCount == 1) {
289             ipCameraHandler.setChannelState(CHANNEL_ITEM_LEFT, OnOffType.OFF);
290             leftCount--;
291         }
292         if (takenCount > 1) {
293             takenCount--;
294         } else if (takenCount == 1) {
295             ipCameraHandler.setChannelState(CHANNEL_ITEM_TAKEN, OnOffType.OFF);
296             takenCount--;
297         }
298         if (faceCount > 1) {
299             faceCount--;
300         } else if (faceCount == 1) {
301             ipCameraHandler.setChannelState(CHANNEL_FACE_DETECTED, OnOffType.OFF);
302             faceCount--;
303         }
304         if (pirCount > 1) {
305             pirCount--;
306         } else if (pirCount == 1) {
307             ipCameraHandler.setChannelState(CHANNEL_PIR_ALARM, OnOffType.OFF);
308             pirCount--;
309         }
310         if (fieldCount > 1) {
311             fieldCount--;
312         } else if (fieldCount == 1) {
313             ipCameraHandler.setChannelState(CHANNEL_FIELD_DETECTION_ALARM, OnOffType.OFF);
314             fieldCount--;
315         }
316         if (fieldCount == 0 && pirCount == 0 && faceCount == 0 && takenCount == 0 && leftCount == 0 && vmdCount == 0
317                 && lineCount == 0) {
318             ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
319         }
320     }
321
322     public void hikSendXml(String httpPutURL, String xml) {
323         logger.trace("Body for PUT:{} is going to be:{}", httpPutURL, xml);
324         FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, new HttpMethod("PUT"), httpPutURL);
325         request.headers().set(HttpHeaderNames.HOST, ipCameraHandler.cameraConfig.getIp());
326         request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
327         request.headers().add(HttpHeaderNames.CONTENT_TYPE, "application/xml; charset=\"UTF-8\"");
328         ByteBuf bbuf = Unpooled.copiedBuffer(xml, StandardCharsets.UTF_8);
329         request.headers().set(HttpHeaderNames.CONTENT_LENGTH, bbuf.readableBytes());
330         request.content().clear().writeBytes(bbuf);
331         ipCameraHandler.sendHttpPUT(httpPutURL, request);
332     }
333
334     public void hikChangeSetting(String httpGetPutURL, String removeElement, String replaceRemovedElementWith) {
335         ChannelTracking localTracker = ipCameraHandler.channelTrackingMap.get(httpGetPutURL);
336         if (localTracker == null) {
337             ipCameraHandler.sendHttpGET(httpGetPutURL);
338             logger.debug(
339                     "Did not have a reply stored before hikChangeSetting was run, try again shortly as a reply has just been requested.");
340             return;
341         }
342         String body = localTracker.getReply();
343         if (body.isEmpty()) {
344             logger.debug(
345                     "Did not have a reply stored before hikChangeSetting was run, try again shortly as a reply has just been requested.");
346             ipCameraHandler.sendHttpGET(httpGetPutURL);
347         } else {
348             logger.trace("An OLD reply from the camera was:{}", body);
349             if (body.contains("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")) {
350                 body = body.substring("<?xml version=\"1.0\" encoding=\"UTF-8\"?>".length());
351             }
352             int elementIndexStart = body.indexOf("<" + removeElement + ">");
353             int elementIndexEnd = body.indexOf("</" + removeElement + ">");
354             body = body.substring(0, elementIndexStart) + replaceRemovedElementWith
355                     + body.substring(elementIndexEnd + removeElement.length() + 3, body.length());
356             logger.trace("Body for this PUT is going to be:{}", body);
357             localTracker.setReply(body);
358             FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, new HttpMethod("PUT"),
359                     httpGetPutURL);
360             request.headers().set(HttpHeaderNames.HOST, ipCameraHandler.cameraConfig.getIp());
361             request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
362             request.headers().add(HttpHeaderNames.CONTENT_TYPE, "application/xml; charset=\"UTF-8\"");
363             ByteBuf bbuf = Unpooled.copiedBuffer(body, StandardCharsets.UTF_8);
364             request.headers().set(HttpHeaderNames.CONTENT_LENGTH, bbuf.readableBytes());
365             request.content().clear().writeBytes(bbuf);
366             ipCameraHandler.sendHttpPUT(httpGetPutURL, request);
367         }
368     }
369
370     // This handles the commands that come from the Openhab event bus.
371     public void handleCommand(ChannelUID channelUID, Command command) {
372         if (command instanceof RefreshType) {
373             switch (channelUID.getId()) {
374                 case CHANNEL_ENABLE_AUDIO_ALARM:
375                     ipCameraHandler.sendHttpGET("/ISAPI/Smart/AudioDetection/channels/" + nvrChannel + "01");
376                     return;
377                 case CHANNEL_ENABLE_LINE_CROSSING_ALARM:
378                     ipCameraHandler.sendHttpGET("/ISAPI/Smart/LineDetection/" + nvrChannel + "01");
379                     return;
380                 case CHANNEL_ENABLE_FIELD_DETECTION_ALARM:
381                     ipCameraHandler.logger.debug("FieldDetection command");
382                     ipCameraHandler.sendHttpGET("/ISAPI/Smart/FieldDetection/" + nvrChannel + "01");
383                     return;
384                 case CHANNEL_ENABLE_MOTION_ALARM:
385                     ipCameraHandler
386                             .sendHttpGET("/ISAPI/System/Video/inputs/channels/" + nvrChannel + "01/motionDetection");
387                     return;
388                 case CHANNEL_TEXT_OVERLAY:
389                     ipCameraHandler
390                             .sendHttpGET("/ISAPI/System/Video/inputs/channels/" + nvrChannel + "/overlays/text/1");
391                     return;
392                 case CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT:
393                     ipCameraHandler.sendHttpGET("/ISAPI/System/IO/inputs/" + nvrChannel);
394                     return;
395                 case CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT:
396                     ipCameraHandler.sendHttpGET("/ISAPI/System/IO/inputs/" + nvrChannel);
397                     return;
398             }
399             return; // Return as we have handled the refresh command above and don't need to
400                     // continue further.
401         } // end of "REFRESH"
402         switch (channelUID.getId()) {
403             case CHANNEL_TEXT_OVERLAY:
404                 logger.debug("Changing text overlay to {}", command.toString());
405                 if (command.toString().isEmpty()) {
406                     hikChangeSetting("/ISAPI/System/Video/inputs/channels/" + nvrChannel + "/overlays/text/1",
407                             "enabled", "<enabled>false</enabled>");
408                 } else {
409                     hikChangeSetting("/ISAPI/System/Video/inputs/channels/" + nvrChannel + "/overlays/text/1",
410                             "displayText", "<displayText>" + command.toString() + "</displayText>");
411                     hikChangeSetting("/ISAPI/System/Video/inputs/channels/" + nvrChannel + "/overlays/text/1",
412                             "enabled", "<enabled>true</enabled>");
413                 }
414                 return;
415             case CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT:
416                 logger.debug("Changing enabled state of the external input 1 to {}", command.toString());
417                 if (OnOffType.ON.equals(command)) {
418                     hikChangeSetting("/ISAPI/System/IO/inputs/" + nvrChannel, "enabled", "<enabled>true</enabled>");
419                 } else {
420                     hikChangeSetting("/ISAPI/System/IO/inputs/" + nvrChannel, "enabled", "<enabled>false</enabled>");
421                 }
422                 return;
423             case CHANNEL_TRIGGER_EXTERNAL_ALARM_INPUT:
424                 logger.debug("Changing triggering state of the external input 1 to {}", command.toString());
425                 if (OnOffType.OFF.equals(command)) {
426                     hikChangeSetting("/ISAPI/System/IO/inputs/" + nvrChannel, "triggering",
427                             "<triggering>low</triggering>");
428                 } else {
429                     hikChangeSetting("/ISAPI/System/IO/inputs/" + nvrChannel, "triggering",
430                             "<triggering>high</triggering>");
431                 }
432                 return;
433             case CHANNEL_ENABLE_PIR_ALARM:
434                 if (OnOffType.ON.equals(command)) {
435                     hikChangeSetting("/ISAPI/WLAlarm/PIR", "enabled", "<enabled>true</enabled>");
436                 } else {
437                     hikChangeSetting("/ISAPI/WLAlarm/PIR", "enabled", "<enabled>false</enabled>");
438                 }
439                 return;
440             case CHANNEL_ENABLE_AUDIO_ALARM:
441                 if (OnOffType.ON.equals(command)) {
442                     hikChangeSetting("/ISAPI/Smart/AudioDetection/channels/" + nvrChannel + "01", "enabled",
443                             "<enabled>true</enabled>");
444                 } else {
445                     hikChangeSetting("/ISAPI/Smart/AudioDetection/channels/" + nvrChannel + "01", "enabled",
446                             "<enabled>false</enabled>");
447                 }
448                 return;
449             case CHANNEL_ENABLE_LINE_CROSSING_ALARM:
450                 if (OnOffType.ON.equals(command)) {
451                     hikChangeSetting("/ISAPI/Smart/LineDetection/" + nvrChannel + "01", "enabled",
452                             "<enabled>true</enabled>");
453                 } else {
454                     hikChangeSetting("/ISAPI/Smart/LineDetection/" + nvrChannel + "01", "enabled",
455                             "<enabled>false</enabled>");
456                 }
457                 return;
458             case CHANNEL_ENABLE_MOTION_ALARM:
459                 if (OnOffType.ON.equals(command)) {
460                     hikChangeSetting("/ISAPI/System/Video/inputs/channels/" + nvrChannel + "01/motionDetection",
461                             "enabled", "<enabled>true</enabled>");
462                 } else {
463                     hikChangeSetting("/ISAPI/System/Video/inputs/channels/" + nvrChannel + "01/motionDetection",
464                             "enabled", "<enabled>false</enabled>");
465                 }
466                 return;
467             case CHANNEL_ENABLE_FIELD_DETECTION_ALARM:
468                 if (OnOffType.ON.equals(command)) {
469                     hikChangeSetting("/ISAPI/Smart/FieldDetection/" + nvrChannel + "01", "enabled",
470                             "<enabled>true</enabled>");
471                 } else {
472                     hikChangeSetting("/ISAPI/Smart/FieldDetection/" + nvrChannel + "01", "enabled",
473                             "<enabled>false</enabled>");
474                 }
475                 return;
476             case CHANNEL_ACTIVATE_ALARM_OUTPUT:
477                 if (OnOffType.ON.equals(command)) {
478                     hikSendXml("/ISAPI/System/IO/outputs/" + nvrChannel + "/trigger",
479                             "<IOPortData version=\"1.0\" xmlns=\"http://www.hikvision.com/ver10/XMLSchema\">\r\n    <outputState>high</outputState>\r\n</IOPortData>\r\n");
480                 } else {
481                     hikSendXml("/ISAPI/System/IO/outputs/" + nvrChannel + "/trigger",
482                             "<IOPortData version=\"1.0\" xmlns=\"http://www.hikvision.com/ver10/XMLSchema\">\r\n    <outputState>low</outputState>\r\n</IOPortData>\r\n");
483                 }
484                 return;
485         }
486     }
487 }