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