2 * Copyright (c) 2010-2022 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.library.types.StringType;
26 import org.openhab.core.thing.ChannelUID;
27 import org.openhab.core.types.Command;
28 import org.openhab.core.types.RefreshType;
29 import org.openhab.core.types.UnDefType;
31 import io.netty.channel.ChannelDuplexHandler;
32 import io.netty.channel.ChannelHandlerContext;
33 import io.netty.util.ReferenceCountUtil;
36 * The {@link DahuaHandler} is responsible for handling commands, which are
37 * sent to one of the channels.
39 * @author Matthew Skinner - Initial contribution
43 public class DahuaHandler extends ChannelDuplexHandler {
44 private IpCameraHandler ipCameraHandler;
45 private int nvrChannel;
47 public DahuaHandler(IpCameraHandler handler, int nvrChannel) {
48 ipCameraHandler = handler;
49 this.nvrChannel = nvrChannel;
52 private void processEvent(String content) {
53 int startIndex = content.indexOf("Code=", 12) + 5;// skip --myboundary and Code=
54 int endIndex = content.indexOf(";", startIndex + 1);
55 if (startIndex == -1 || endIndex == -1) {
56 ipCameraHandler.logger.debug("Code= not found in Dahua event. Content was:{}", content);
59 String code = content.substring(startIndex, endIndex);
60 startIndex = endIndex + 8;// skip ;action=
61 endIndex = content.indexOf(";", startIndex);
62 if (startIndex == -1 || endIndex == -1) {
63 ipCameraHandler.logger.debug(";action= not found in Dahua event. Content was:{}", content);
66 String action = content.substring(startIndex, endIndex);
67 startIndex = content.indexOf(";data=", startIndex);
69 endIndex = content.lastIndexOf("}");
71 String data = content.substring(startIndex + 6, endIndex + 1);
72 ipCameraHandler.setChannelState(CHANNEL_LAST_EVENT_DATA, new StringType(data));
77 if ("Start".equals(action)) {
78 ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
79 } else if ("Stop".equals(action)) {
80 ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
83 case "TakenAwayDetection":
84 if ("Start".equals(action)) {
85 ipCameraHandler.motionDetected(CHANNEL_ITEM_TAKEN);
86 } else if ("Stop".equals(action)) {
87 ipCameraHandler.noMotionDetected(CHANNEL_ITEM_TAKEN);
91 if ("Start".equals(action)) {
92 ipCameraHandler.motionDetected(CHANNEL_ITEM_LEFT);
93 } else if ("Stop".equals(action)) {
94 ipCameraHandler.noMotionDetected(CHANNEL_ITEM_LEFT);
97 case "SmartMotionVehicle":
98 if ("Start".equals(action)) {
99 ipCameraHandler.motionDetected(CHANNEL_CAR_ALARM);
100 } else if ("Stop".equals(action)) {
101 ipCameraHandler.noMotionDetected(CHANNEL_CAR_ALARM);
104 case "SmartMotionHuman":
105 if ("Start".equals(action)) {
106 ipCameraHandler.motionDetected(CHANNEL_HUMAN_ALARM);
107 } else if ("Stop".equals(action)) {
108 ipCameraHandler.noMotionDetected(CHANNEL_HUMAN_ALARM);
111 case "CrossLineDetection":
112 if ("Start".equals(action)) {
113 ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM);
114 } else if ("Stop".equals(action)) {
115 ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM);
119 case "AudioMutation":
120 if ("Start".equals(action)) {
121 ipCameraHandler.audioDetected();
122 } else if ("Stop".equals(action)) {
123 ipCameraHandler.noAudioDetected();
126 case "FaceDetection":
127 if ("Start".equals(action)) {
128 ipCameraHandler.motionDetected(CHANNEL_FACE_DETECTED);
129 } else if ("Stop".equals(action)) {
130 ipCameraHandler.noMotionDetected(CHANNEL_FACE_DETECTED);
133 case "ParkingDetection":
134 if ("Start".equals(action)) {
135 ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.ON);
136 } else if ("Stop".equals(action)) {
137 ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.OFF);
140 case "CrossRegionDetection":
141 if ("Start".equals(action)) {
142 ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM);
143 } else if ("Stop".equals(action)) {
144 ipCameraHandler.noMotionDetected(CHANNEL_FIELD_DETECTION_ALARM);
149 if ("Start".equals(action)) {
150 ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.ON);
151 } else if ("Stop".equals(action)) {
152 ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.OFF);
155 case "VideoAbnormalDetection":
156 if ("Start".equals(action)) {
157 ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.ON);
158 } else if ("Stop".equals(action)) {
159 ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.OFF);
163 if ("Start".equals(action)) {
164 ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.ON);
165 } else if ("Stop".equals(action)) {
166 ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.OFF);
170 if ("Start".equals(action)) {
171 if (content.contains("index=0")) {
172 ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.ON);
174 ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.ON);
176 } else if ("Stop".equals(action)) {
177 if (content.contains("index=0")) {
178 ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
180 ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.OFF);
185 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.ON);
187 case "LensMaskClose":
188 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.OFF);
190 // Skip these so they are not logged.
193 case "NTPAdjustTime":
194 case "StorageChange":
197 case "VideoMotionInfo":
198 case "RtspSessionDisconnect":
199 case "LeFunctionStatusSync":
203 ipCameraHandler.logger.debug("Unrecognised Dahua event, Code={}, action={}", code, action);
207 // This handles the incoming http replies back from the camera.
209 public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception {
210 if (msg == null || ctx == null) {
214 String content = msg.toString();
215 if (content.startsWith("--myboundary") || content.startsWith("-- myboundary")) {
216 processEvent(content);
219 ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content);
220 // determine if the motion detection is turned on or off.
221 if (content.contains("table.MotionDetect[0].Enable=true")) {
222 ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.ON);
223 } else if (content.contains("table.MotionDetect[" + nvrChannel + "].Enable=false")) {
224 ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.OFF);
227 // determine if the audio alarm is turned on or off.
228 if (content.contains("table.AudioDetect[0].MutationDetect=true")) {
229 ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.ON);
230 } else if (content.contains("table.AudioDetect[0].MutationDetect=false")) {
231 ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.OFF);
234 // Handle AudioMutationThreshold alarm
235 if (content.contains("table.AudioDetect[0].MutationThreold=")) {
236 String value = ipCameraHandler.returnValueFromString(content, "table.AudioDetect[0].MutationThreold=");
237 ipCameraHandler.setChannelState(CHANNEL_THRESHOLD_AUDIO_ALARM, PercentType.valueOf(value));
240 // CrossLineDetection alarm on/off
241 if (content.contains("table.VideoAnalyseRule[0][1].Enable=true")) {
242 ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.ON);
243 } else if (content.contains("table.VideoAnalyseRule[0][1].Enable=false")) {
244 ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.OFF);
246 // Privacy Mode on/off
247 if (content.contains("table.LeLensMask[0].Enable=true")) {
248 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.ON);
249 } else if (content.contains("table.LeLensMask[0].Enable=false")) {
250 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.OFF);
253 ReferenceCountUtil.release(msg);
257 // This handles the commands that come from the openHAB event bus.
258 public void handleCommand(ChannelUID channelUID, Command command) {
259 if (command instanceof RefreshType) {
260 switch (channelUID.getId()) {
261 case CHANNEL_ENABLE_AUDIO_ALARM:
262 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=AudioDetect[0]");
264 case CHANNEL_ENABLE_LINE_CROSSING_ALARM:
265 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=VideoAnalyseRule");
267 case CHANNEL_ENABLE_MOTION_ALARM:
268 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=MotionDetect[0]");
270 case CHANNEL_ENABLE_PRIVACY_MODE:
271 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=LeLensMask[0]");
275 } // end of "REFRESH"
276 switch (channelUID.getId()) {
277 case CHANNEL_TEXT_OVERLAY:
278 String text = Helper.encodeSpecialChars(command.toString());
279 if (text.isEmpty()) {
280 ipCameraHandler.sendHttpGET(
281 "/cgi-bin/configManager.cgi?action=setConfig&VideoWidget[0].CustomTitle[1].EncodeBlend=false");
283 ipCameraHandler.sendHttpGET(
284 "/cgi-bin/configManager.cgi?action=setConfig&VideoWidget[0].CustomTitle[1].EncodeBlend=true&VideoWidget[0].CustomTitle[1].Text="
288 case CHANNEL_ENABLE_LED:
289 ipCameraHandler.setChannelState(CHANNEL_AUTO_LED, OnOffType.OFF);
290 if (DecimalType.ZERO.equals(command) || OnOffType.OFF.equals(command)) {
291 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Off");
292 } else if (OnOffType.ON.equals(command)) {
294 .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Manual");
296 ipCameraHandler.sendHttpGET(
297 "/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Manual&Lighting[0][0].MiddleLight[0].Light="
298 + command.toString());
301 case CHANNEL_AUTO_LED:
302 if (OnOffType.ON.equals(command)) {
303 ipCameraHandler.setChannelState(CHANNEL_ENABLE_LED, UnDefType.UNDEF);
304 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Auto");
307 case CHANNEL_THRESHOLD_AUDIO_ALARM:
308 int threshold = Math.round(Float.valueOf(command.toString()));
310 if (threshold == 0) {
311 ipCameraHandler.sendHttpGET(
312 "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationThreold=1");
314 ipCameraHandler.sendHttpGET(
315 "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationThreold=" + threshold);
318 case CHANNEL_ENABLE_AUDIO_ALARM:
319 if (OnOffType.ON.equals(command)) {
320 ipCameraHandler.sendHttpGET(
321 "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationDetect=true&AudioDetect[0].EventHandler.Dejitter=1");
323 ipCameraHandler.sendHttpGET(
324 "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationDetect=false");
327 case CHANNEL_ENABLE_LINE_CROSSING_ALARM:
328 if (OnOffType.ON.equals(command)) {
329 ipCameraHandler.sendHttpGET(
330 "/cgi-bin/configManager.cgi?action=setConfig&VideoAnalyseRule[0][1].Enable=true");
332 ipCameraHandler.sendHttpGET(
333 "/cgi-bin/configManager.cgi?action=setConfig&VideoAnalyseRule[0][1].Enable=false");
336 case CHANNEL_ENABLE_MOTION_ALARM:
337 if (OnOffType.ON.equals(command)) {
338 ipCameraHandler.sendHttpGET(
339 "/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[0].Enable=true&MotionDetect[0].EventHandler.Dejitter=1");
342 .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[0].Enable=false");
345 case CHANNEL_ACTIVATE_ALARM_OUTPUT:
346 if (OnOffType.ON.equals(command)) {
347 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[0].Mode=1");
349 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[0].Mode=0");
352 case CHANNEL_ACTIVATE_ALARM_OUTPUT2:
353 if (OnOffType.ON.equals(command)) {
354 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[1].Mode=1");
356 ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[1].Mode=0");
359 case CHANNEL_ENABLE_PRIVACY_MODE:
360 if (OnOffType.OFF.equals(command)) {
362 .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&LeLensMask[0].Enable=false");
363 } else if (OnOffType.ON.equals(command)) {
365 .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&LeLensMask[0].Enable=true");
371 // If a camera does not need to poll a request as often as snapshots, it can be
372 // added here. Binding steps through the list.
373 public List<String> getLowPriorityRequests() {