]> git.basschouten.com Git - openhab-addons.git/blob
ad4ce9d67cebf99d554a418969fe903e73162ef2
[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.util.List;
18 import java.util.regex.Pattern;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
23 import org.openhab.core.library.types.DecimalType;
24 import org.openhab.core.library.types.OnOffType;
25 import org.openhab.core.library.types.PercentType;
26 import org.openhab.core.library.types.StringType;
27 import org.openhab.core.thing.ChannelUID;
28 import org.openhab.core.types.Command;
29 import org.openhab.core.types.RefreshType;
30 import org.openhab.core.types.UnDefType;
31
32 import io.netty.channel.ChannelDuplexHandler;
33 import io.netty.channel.ChannelHandlerContext;
34 import io.netty.util.ReferenceCountUtil;
35
36 /**
37  * The {@link DahuaHandler} is responsible for handling commands, which are
38  * sent to one of the channels.
39  *
40  * @author Matthew Skinner - Initial contribution
41  */
42
43 @NonNullByDefault
44 public class DahuaHandler extends ChannelDuplexHandler {
45     private IpCameraHandler ipCameraHandler;
46     private int nvrChannel;
47     private Pattern boundaryPattern;
48
49     public DahuaHandler(IpCameraHandler handler, int nvrChannel) {
50         ipCameraHandler = handler;
51         this.nvrChannel = nvrChannel;
52         boundaryPattern = Pattern.compile("^-- ?myboundary$", Pattern.MULTILINE);
53     }
54
55     private void processEvent(String content) {
56         int startIndex = content.indexOf("Code=") + 5;// skip Code=
57         int endIndex = content.indexOf(";", startIndex + 1);
58         if (startIndex == -1 || endIndex == -1) {
59             ipCameraHandler.logger.debug("Code= not found in Dahua event. Content was:{}", content);
60             return;
61         }
62         String code = content.substring(startIndex, endIndex);
63         startIndex = endIndex + 8;// skip ;action=
64         endIndex = content.indexOf(";", startIndex);
65         if (startIndex == -1 || endIndex == -1) {
66             ipCameraHandler.logger.debug(";action= not found in Dahua event. Content was:{}", content);
67             return;
68         }
69         String action = content.substring(startIndex, endIndex);
70         startIndex = content.indexOf(";data=", startIndex);
71         if (startIndex > 0) {
72             endIndex = content.lastIndexOf("}");
73             if (endIndex > 0) {
74                 String data = content.substring(startIndex + 6, endIndex + 1);
75                 ipCameraHandler.setChannelState(CHANNEL_LAST_EVENT_DATA, new StringType(data));
76             }
77         }
78         switch (code) {
79             case "VideoMotion":
80                 if ("Start".equals(action)) {
81                     ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
82                 } else if ("Stop".equals(action)) {
83                     ipCameraHandler.noMotionDetected(CHANNEL_MOTION_ALARM);
84                 }
85                 break;
86             case "TakenAwayDetection":
87                 if ("Start".equals(action)) {
88                     ipCameraHandler.motionDetected(CHANNEL_ITEM_TAKEN);
89                 } else if ("Stop".equals(action)) {
90                     ipCameraHandler.noMotionDetected(CHANNEL_ITEM_TAKEN);
91                 }
92                 break;
93             case "LeftDetection":
94                 if ("Start".equals(action)) {
95                     ipCameraHandler.motionDetected(CHANNEL_ITEM_LEFT);
96                 } else if ("Stop".equals(action)) {
97                     ipCameraHandler.noMotionDetected(CHANNEL_ITEM_LEFT);
98                 }
99                 break;
100             case "SmartMotionVehicle":
101                 if ("Start".equals(action)) {
102                     ipCameraHandler.motionDetected(CHANNEL_CAR_ALARM);
103                 } else if ("Stop".equals(action)) {
104                     ipCameraHandler.noMotionDetected(CHANNEL_CAR_ALARM);
105                 }
106                 break;
107             case "SmartMotionHuman":
108                 if ("Start".equals(action)) {
109                     ipCameraHandler.motionDetected(CHANNEL_HUMAN_ALARM);
110                 } else if ("Stop".equals(action)) {
111                     ipCameraHandler.noMotionDetected(CHANNEL_HUMAN_ALARM);
112                 }
113                 break;
114             case "CrossLineDetection":
115                 if ("Start".equals(action)) {
116                     ipCameraHandler.motionDetected(CHANNEL_LINE_CROSSING_ALARM);
117                 } else if ("Stop".equals(action)) {
118                     ipCameraHandler.noMotionDetected(CHANNEL_LINE_CROSSING_ALARM);
119                 }
120                 break;
121             case "AudioAnomaly":
122             case "AudioMutation":
123                 if ("Start".equals(action)) {
124                     ipCameraHandler.audioDetected();
125                 } else if ("Stop".equals(action)) {
126                     ipCameraHandler.noAudioDetected();
127                 }
128                 break;
129             case "FaceDetection":
130                 if ("Start".equals(action)) {
131                     ipCameraHandler.motionDetected(CHANNEL_FACE_DETECTED);
132                 } else if ("Stop".equals(action)) {
133                     ipCameraHandler.noMotionDetected(CHANNEL_FACE_DETECTED);
134                 }
135                 break;
136             case "ParkingDetection":
137                 if ("Start".equals(action)) {
138                     ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.ON);
139                 } else if ("Stop".equals(action)) {
140                     ipCameraHandler.setChannelState(CHANNEL_PARKING_ALARM, OnOffType.OFF);
141                 }
142                 break;
143             case "CrossRegionDetection":
144                 if ("Start".equals(action)) {
145                     ipCameraHandler.motionDetected(CHANNEL_FIELD_DETECTION_ALARM);
146                 } else if ("Stop".equals(action)) {
147                     ipCameraHandler.noMotionDetected(CHANNEL_FIELD_DETECTION_ALARM);
148                 }
149                 break;
150             case "VideoLoss":
151             case "VideoBlind":
152                 if ("Start".equals(action)) {
153                     ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.ON);
154                 } else if ("Stop".equals(action)) {
155                     ipCameraHandler.setChannelState(CHANNEL_TOO_DARK_ALARM, OnOffType.OFF);
156                 }
157                 break;
158             case "VideoAbnormalDetection":
159                 if ("Start".equals(action)) {
160                     ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.ON);
161                 } else if ("Stop".equals(action)) {
162                     ipCameraHandler.setChannelState(CHANNEL_SCENE_CHANGE_ALARM, OnOffType.OFF);
163                 }
164                 break;
165             case "VideoUnFocus":
166                 if ("Start".equals(action)) {
167                     ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.ON);
168                 } else if ("Stop".equals(action)) {
169                     ipCameraHandler.setChannelState(CHANNEL_TOO_BLURRY_ALARM, OnOffType.OFF);
170                 }
171                 break;
172             case "AlarmLocal":
173                 if ("Start".equals(action)) {
174                     if (content.contains("index=0")) {
175                         ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.ON);
176                     } else {
177                         ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.ON);
178                     }
179                 } else if ("Stop".equals(action)) {
180                     if (content.contains("index=0")) {
181                         ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
182                     } else {
183                         ipCameraHandler.setChannelState(CHANNEL_EXTERNAL_ALARM_INPUT2, OnOffType.OFF);
184                     }
185                 }
186                 break;
187             case "LensMaskOpen":
188                 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.ON);
189                 break;
190             case "LensMaskClose":
191                 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.OFF);
192                 break;
193             // Skip these so they are not logged.
194             case "TimeChange":
195             case "IntelliFrame":
196             case "NTPAdjustTime":
197             case "StorageChange":
198             case "Reboot":
199             case "NewFile":
200             case "VideoMotionInfo":
201             case "RtspSessionDisconnect":
202             case "LeFunctionStatusSync":
203             case "RecordDelete":
204                 break;
205             default:
206                 ipCameraHandler.logger.debug("Unrecognised Dahua event, Code={}, action={}", code, action);
207         }
208     }
209
210     private void processSettings(String content) {
211         // determine if the motion detection is turned on or off.
212         if (content.contains("table.MotionDetect[0].Enable=true")) {
213             ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.ON);
214         } else if (content.contains("table.MotionDetect[" + nvrChannel + "].Enable=false")) {
215             ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.OFF);
216         }
217
218         // determine if the audio alarm is turned on or off.
219         if (content.contains("table.AudioDetect[0].MutationDetect=true")) {
220             ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.ON);
221         } else if (content.contains("table.AudioDetect[0].MutationDetect=false")) {
222             ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.OFF);
223         }
224
225         // Handle AudioMutationThreshold alarm
226         if (content.contains("table.AudioDetect[0].MutationThreold=")) {
227             String value = ipCameraHandler.returnValueFromString(content, "table.AudioDetect[0].MutationThreold=");
228             ipCameraHandler.setChannelState(CHANNEL_THRESHOLD_AUDIO_ALARM, PercentType.valueOf(value));
229         }
230
231         // CrossLineDetection alarm on/off
232         if (content.contains("table.VideoAnalyseRule[0][1].Enable=true")) {
233             ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.ON);
234         } else if (content.contains("table.VideoAnalyseRule[0][1].Enable=false")) {
235             ipCameraHandler.setChannelState(CHANNEL_ENABLE_LINE_CROSSING_ALARM, OnOffType.OFF);
236         }
237         // Privacy Mode on/off
238         if (content.contains("table.LeLensMask[0].Enable=true")) {
239             ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.ON);
240         } else if (content.contains("table.LeLensMask[0].Enable=false")) {
241             ipCameraHandler.setChannelState(CHANNEL_ENABLE_PRIVACY_MODE, OnOffType.OFF);
242         }
243     }
244
245     // This handles the incoming http replies back from the camera.
246     @Override
247     public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception {
248         if (msg == null || ctx == null) {
249             return;
250         }
251         try {
252             String content = msg.toString();
253             ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content);
254             String[] events = boundaryPattern.split(content);
255             if (events.length > 1) {
256                 for (int i = 1; i < events.length; i++) {
257                     processEvent(events[i]);
258                 }
259             } else {
260                 processSettings(content);
261             }
262         } finally {
263             ReferenceCountUtil.release(msg);
264         }
265     }
266
267     // This handles the commands that come from the openHAB event bus.
268     public void handleCommand(ChannelUID channelUID, Command command) {
269         if (command instanceof RefreshType) {
270             switch (channelUID.getId()) {
271                 case CHANNEL_ENABLE_AUDIO_ALARM:
272                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=AudioDetect[0]");
273                     return;
274                 case CHANNEL_ENABLE_LINE_CROSSING_ALARM:
275                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=VideoAnalyseRule");
276                     return;
277                 case CHANNEL_ENABLE_MOTION_ALARM:
278                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=MotionDetect[0]");
279                     return;
280                 case CHANNEL_ENABLE_PRIVACY_MODE:
281                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=getConfig&name=LeLensMask[0]");
282                     return;
283             }
284             return;
285         } // end of "REFRESH"
286         switch (channelUID.getId()) {
287             case CHANNEL_TEXT_OVERLAY:
288                 String text = Helper.encodeSpecialChars(command.toString());
289                 if (text.isEmpty()) {
290                     ipCameraHandler.sendHttpGET(
291                             "/cgi-bin/configManager.cgi?action=setConfig&VideoWidget[0].CustomTitle[1].EncodeBlend=false");
292                 } else {
293                     ipCameraHandler.sendHttpGET(
294                             "/cgi-bin/configManager.cgi?action=setConfig&VideoWidget[0].CustomTitle[1].EncodeBlend=true&VideoWidget[0].CustomTitle[1].Text="
295                                     + text);
296                 }
297                 return;
298             case CHANNEL_ENABLE_LED:
299                 ipCameraHandler.setChannelState(CHANNEL_AUTO_LED, OnOffType.OFF);
300                 if (DecimalType.ZERO.equals(command) || OnOffType.OFF.equals(command)) {
301                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Off");
302                 } else if (OnOffType.ON.equals(command)) {
303                     ipCameraHandler
304                             .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Manual");
305                 } else {
306                     ipCameraHandler.sendHttpGET(
307                             "/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Manual&Lighting[0][0].MiddleLight[0].Light="
308                                     + command.toString());
309                 }
310                 return;
311             case CHANNEL_AUTO_LED:
312                 if (OnOffType.ON.equals(command)) {
313                     ipCameraHandler.setChannelState(CHANNEL_ENABLE_LED, UnDefType.UNDEF);
314                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&Lighting[0][0].Mode=Auto");
315                 }
316                 return;
317             case CHANNEL_THRESHOLD_AUDIO_ALARM:
318                 int threshold = Math.round(Float.valueOf(command.toString()));
319
320                 if (threshold == 0) {
321                     ipCameraHandler.sendHttpGET(
322                             "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationThreold=1");
323                 } else {
324                     ipCameraHandler.sendHttpGET(
325                             "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationThreold=" + threshold);
326                 }
327                 return;
328             case CHANNEL_ENABLE_AUDIO_ALARM:
329                 if (OnOffType.ON.equals(command)) {
330                     ipCameraHandler.sendHttpGET(
331                             "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationDetect=true&AudioDetect[0].EventHandler.Dejitter=1");
332                 } else {
333                     ipCameraHandler.sendHttpGET(
334                             "/cgi-bin/configManager.cgi?action=setConfig&AudioDetect[0].MutationDetect=false");
335                 }
336                 return;
337             case CHANNEL_ENABLE_LINE_CROSSING_ALARM:
338                 if (OnOffType.ON.equals(command)) {
339                     ipCameraHandler.sendHttpGET(
340                             "/cgi-bin/configManager.cgi?action=setConfig&VideoAnalyseRule[0][1].Enable=true");
341                 } else {
342                     ipCameraHandler.sendHttpGET(
343                             "/cgi-bin/configManager.cgi?action=setConfig&VideoAnalyseRule[0][1].Enable=false");
344                 }
345                 return;
346             case CHANNEL_ENABLE_MOTION_ALARM:
347                 if (OnOffType.ON.equals(command)) {
348                     ipCameraHandler.sendHttpGET(
349                             "/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[0].Enable=true&MotionDetect[0].EventHandler.Dejitter=1");
350                 } else {
351                     ipCameraHandler
352                             .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&MotionDetect[0].Enable=false");
353                 }
354                 return;
355             case CHANNEL_ACTIVATE_ALARM_OUTPUT:
356                 if (OnOffType.ON.equals(command)) {
357                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[0].Mode=1");
358                 } else {
359                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[0].Mode=0");
360                 }
361                 return;
362             case CHANNEL_ACTIVATE_ALARM_OUTPUT2:
363                 if (OnOffType.ON.equals(command)) {
364                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[1].Mode=1");
365                 } else {
366                     ipCameraHandler.sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&AlarmOut[1].Mode=0");
367                 }
368                 return;
369             case CHANNEL_ENABLE_PRIVACY_MODE:
370                 if (OnOffType.OFF.equals(command)) {
371                     ipCameraHandler
372                             .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&LeLensMask[0].Enable=false");
373                 } else if (OnOffType.ON.equals(command)) {
374                     ipCameraHandler
375                             .sendHttpGET("/cgi-bin/configManager.cgi?action=setConfig&LeLensMask[0].Enable=true");
376                 }
377                 return;
378         }
379     }
380
381     // If a camera does not need to poll a request as often as snapshots, it can be
382     // added here. Binding steps through the list.
383     public List<String> getLowPriorityRequests() {
384         return List.of();
385     }
386 }