2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.ipcamera.internal;
15 import static org.openhab.binding.ipcamera.internal.IpCameraBindingConstants.*;
17 import java.util.List;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
22 import org.openhab.core.library.types.DecimalType;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.library.types.PercentType;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.core.types.Command;
27 import org.openhab.core.types.RefreshType;
28 import org.openhab.core.types.UnDefType;
30 import io.netty.channel.ChannelDuplexHandler;
31 import io.netty.channel.ChannelHandlerContext;
32 import io.netty.util.ReferenceCountUtil;
35 * The {@link DahuaHandler} is responsible for handling commands, which are
36 * sent to one of the channels.
38 * @author Matthew Skinner - Initial contribution
42 public class DahuaHandler extends ChannelDuplexHandler {
43 private IpCameraHandler ipCameraHandler;
44 private int nvrChannel;
46 public DahuaHandler(IpCameraHandler handler, int nvrChannel) {
47 ipCameraHandler = handler;
48 this.nvrChannel = nvrChannel;
51 private void processEvent(String content) {
52 int startIndex = content.indexOf("Code=", 12) + 5;// skip --myboundary and Code=
53 int endIndex = content.indexOf(";", startIndex + 1);
54 if (startIndex == -1 || endIndex == -1) {
55 ipCameraHandler.logger.debug("Code= not found in Dahua event. Content was:{}", content);
58 String code = content.substring(startIndex, endIndex);
59 startIndex = endIndex + 8;// skip ;action=
60 endIndex = content.indexOf(";", startIndex);
61 if (startIndex == -1 || endIndex == -1) {
62 ipCameraHandler.logger.debug(";action= not found in Dahua event. Content was:{}", content);
65 String action = content.substring(startIndex, endIndex);
68 if ("Start".equals(action)) {
69 ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
70 } else if ("Stop".equals(action)) {
71 ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
74 case "TakenAwayDetection":
75 if ("Start".equals(action)) {
76 ipCameraHandler.motionDetected(CHANNEL_ITEM_TAKEN);
77 } else if ("Stop".equals(action)) {
78 ipCameraHandler.noMotionDetected(CHANNEL_ITEM_TAKEN);
82 if ("Start".equals(action)) {
83 ipCameraHandler.motionDetected(CHANNEL_ITEM_LEFT);
84 } else if ("Stop".equals(action)) {
85 ipCameraHandler.noMotionDetected(CHANNEL_ITEM_LEFT);
88 case "SmartMotionVehicle":
89 if ("Start".equals(action)) {
90 ipCameraHandler.motionDetected(CHANNEL_CAR_ALARM);
91 } else if ("Stop".equals(action)) {
92 ipCameraHandler.noMotionDetected(CHANNEL_CAR_ALARM);
95 case "SmartMotionHuman":
96 if ("Start".equals(action)) {
97 ipCameraHandler.motionDetected(CHANNEL_HUMAN_ALARM);
98 } else if ("Stop".equals(action)) {
99 ipCameraHandler.noMotionDetected(CHANNEL_HUMAN_ALARM);
102 case "CrossLineDetection":
103 if ("Start".equals(action)) {
104 ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM);
105 } else if ("Stop".equals(action)) {
106 ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM);
109 case "AudioMutation":
110 if ("Start".equals(action)) {
111 ipCameraHandler.audioDetected();
112 } else if ("Stop".equals(action)) {
113 ipCameraHandler.noAudioDetected();
116 case "FaceDetection":
117 if ("Start".equals(action)) {
118 ipCameraHandler.motionDetected(CHANNEL_FACE_DETECTED);
119 } else if ("Stop".equals(action)) {
120 ipCameraHandler.noMotionDetected(CHANNEL_FACE_DETECTED);
123 case "ParkingDetection":
124 if ("Start".equals(action)) {
125 ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.ON);
126 } else if ("Stop".equals(action)) {
127 ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.OFF);
130 case "CrossRegionDetection":
131 if ("Start".equals(action)) {
132 ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM);
133 } else if ("Stop".equals(action)) {
134 ipCameraHandler.noMotionDetected(CHANNEL_FIELD_DETECTION_ALARM);
139 if ("Start".equals(action)) {
140 ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.ON);
141 } else if ("Stop".equals(action)) {
142 ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.OFF);
145 case "VideoAbnormalDetection":
146 if ("Start".equals(action)) {
147 ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.ON);
148 } else if ("Stop".equals(action)) {
149 ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.OFF);
153 if ("Start".equals(action)) {
154 ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.ON);
155 } else if ("Stop".equals(action)) {
156 ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.OFF);
160 if ("Start".equals(action)) {
161 if (content.contains("index=0")) {
162 ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.ON);
164 ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.ON);
166 } else if ("Stop".equals(action)) {
167 if (content.contains("index=0")) {
168 ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
170 ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.OFF);
175 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.ON);
177 case "LensMaskClose":
178 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.OFF);
180 // Skip these so they are not logged.
183 case "NTPAdjustTime":
184 case "StorageChange":
187 case "VideoMotionInfo":
188 case "RtspSessionDisconnect":
189 case "LeFunctionStatusSync":
193 ipCameraHandler.logger.debug("Unrecognised Dahua event, Code={}, action={}", code, action);
197 // This handles the incoming http replies back from the camera.
199 public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception {
200 if (msg == null || ctx == null) {
204 String content = msg.toString();
205 if (content.startsWith("--myboundary")) {
206 processEvent(content);
209 ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content);
210 // determine if the motion detection is turned on or off.
211 if (content.contains("table.MotionDetect[0].Enable=true")) {
212 ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.ON);
213 } else if (content.contains("table.MotionDetect[" + nvrChannel + "].Enable=false")) {
214 ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.OFF);
217 // determine if the audio alarm is turned on or off.
218 if (content.contains("table.AudioDetect[0].MutationDetect=true")) {
219 ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.ON);
220 } else if (content.contains("table.AudioDetect[0].MutationDetect=false")) {
221 ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.OFF);
224 // Handle AudioMutationThreshold alarm
225 if (content.contains("table.AudioDetect[0].MutationThreold=")) {
226 String value = ipCameraHandler.returnValueFromString(content, "table.AudioDetect[0].MutationThreold=");
227 ipCameraHandler.setChannelState(CHANNEL_THRESHOLD_AUDIO_ALARM, PercentType.valueOf(value));
230 // CrossLineDetection alarm on/off
231 if (content.contains("table.VideoAnalyseRule[0][1].Enable=true")) {
232 ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.ON);
233 } else if (content.contains("table.VideoAnalyseRule[0][1].Enable=false")) {
234 ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.OFF);
236 // Privacy Mode on/off
237 if (content.contains("table.LeLensMask[0].Enable=true")) {
238 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.ON);
239 } else if (content.contains("table.LeLensMask[0].Enable=false")) {
240 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.OFF);
243 ReferenceCountUtil.release(msg);
247 // This handles the commands that come from the openHAB event bus.
248 public void handleCommand(ChannelUID channelUID, Command command) {
249 if (command instanceof RefreshType) {
250 switch (channelUID.getId()) {
251 case CHANNEL_ENABLE_AUDIO_ALARM:
252 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=AudioDetect[0]");
254 case CHANNEL_ENABLE_LINE_CROSSING_ALARM:
255 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=VideoAnalyseRule");
257 case CHANNEL_ENABLE_MOTION_ALARM:
258 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=MotionDetect[0]");
260 case CHANNEL_ENABLE_PRIVACY_MODE:
261 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=LeLensMask[0]");
265 } // end of "REFRESH"
266 switch (channelUID.getId()) {
267 case CHANNEL_TEXT_OVERLAY:
268 String text = Helper.encodeSpecialChars(command.toString());
269 if (text.isEmpty()) {
270 ipCameraHandler.sendHttpGET(
271 "/cgi-bin/configManager.cgi?action=setConfig&VideoWidget[0].CustomTitle[1].EncodeBlend=false");
273 ipCameraHandler.sendHttpGET(
274 "/cgi-bin/configManager.cgi?action=setConfig&VideoWidget[0].CustomTitle[1].EncodeBlend=true&VideoWidget[0].CustomTitle[1].Text="
278 case CHANNEL_ENABLE_LED:
279 ipCameraHandler.setChannelState(CHANNEL_AUTO_LED, OnOffType.OFF);
280 if (DecimalType.ZERO.equals(command) || OnOffType.OFF.equals(command)) {
281 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Off");
282 } else if (OnOffType.ON.equals(command)) {
284 .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Manual");
286 ipCameraHandler.sendHttpGET(
287 "/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Manual&Lighting[0][0].MiddleLight[0].Light="
288 + command.toString());
291 case CHANNEL_AUTO_LED:
292 if (OnOffType.ON.equals(command)) {
293 ipCameraHandler.setChannelState(CHANNEL_ENABLE_LED, UnDefType.UNDEF);
294 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Auto");
297 case CHANNEL_THRESHOLD_AUDIO_ALARM:
298 int threshold = Math.round(Float.valueOf(command.toString()));
300 if (threshold == 0) {
301 ipCameraHandler.sendHttpGET(
302 "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationThreold=1");
304 ipCameraHandler.sendHttpGET(
305 "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationThreold=" + threshold);
308 case CHANNEL_ENABLE_AUDIO_ALARM:
309 if (OnOffType.ON.equals(command)) {
310 ipCameraHandler.sendHttpGET(
311 "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationDetect=true&AudioDetect[0].EventHandler.Dejitter=1");
313 ipCameraHandler.sendHttpGET(
314 "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationDetect=false");
317 case CHANNEL_ENABLE_LINE_CROSSING_ALARM:
318 if (OnOffType.ON.equals(command)) {
319 ipCameraHandler.sendHttpGET(
320 "/cgi-bin/configManager.cgi?action=setConfig&VideoAnalyseRule[0][1].Enable=true");
322 ipCameraHandler.sendHttpGET(
323 "/cgi-bin/configManager.cgi?action=setConfig&VideoAnalyseRule[0][1].Enable=false");
326 case CHANNEL_ENABLE_MOTION_ALARM:
327 if (OnOffType.ON.equals(command)) {
328 ipCameraHandler.sendHttpGET(
329 "/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[0].Enable=true&MotionDetect[0].EventHandler.Dejitter=1");
332 .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[0].Enable=false");
335 case CHANNEL_ACTIVATE_ALARM_OUTPUT:
336 if (OnOffType.ON.equals(command)) {
337 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[0].Mode=1");
339 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[0].Mode=0");
342 case CHANNEL_ACTIVATE_ALARM_OUTPUT2:
343 if (OnOffType.ON.equals(command)) {
344 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[1].Mode=1");
346 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[1].Mode=0");
349 case CHANNEL_ENABLE_PRIVACY_MODE:
350 if (OnOffType.OFF.equals(command)) {
352 .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&LeLensMask[0].Enable=false");
353 } else if (OnOffType.ON.equals(command)) {
355 .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&LeLensMask[0].Enable=true");
361 // If a camera does not need to poll a request as often as snapshots, it can be
362 // added here. Binding steps through the list.
363 public List<String> getLowPriorityRequests() {