2 * Copyright (c) 2010-2023 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.math.BigDecimal;
18 import java.util.ArrayList;
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.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;
30 import io.netty.channel.ChannelDuplexHandler;
31 import io.netty.channel.ChannelHandlerContext;
32 import io.netty.util.ReferenceCountUtil;
35 * The {@link InstarHandler} is responsible for handling commands, which are
36 * sent to one of the channels.
38 * @author Matthew Skinner - Initial contribution
42 public class InstarHandler extends ChannelDuplexHandler {
43 private IpCameraHandler ipCameraHandler;
44 private String requestUrl = "Empty";
46 public InstarHandler(IpCameraHandler thingHandler) {
47 ipCameraHandler = thingHandler;
50 public void setURL(String url) {
54 // This handles the incoming http replies back from the camera.
56 public void channelRead(@Nullable ChannelHandlerContext ctx, @Nullable Object msg) throws Exception {
57 if (msg == null || ctx == null) {
62 String content = msg.toString();
63 ipCameraHandler.logger.trace("HTTP Result back from camera is \t:{}:", content);
65 case "/param.cgi?cmd=getinfrared":
66 if (content.contains("var infraredstat=\"auto") || content.contains("infraredstat=\"2\"")) {
67 ipCameraHandler.setChannelState(CHANNEL_AUTO_LED, OnOffType.ON);
69 ipCameraHandler.setChannelState(CHANNEL_AUTO_LED, OnOffType.OFF);
72 case "/param.cgi?cmd=getoverlayattr&-region=1":// Text Overlays
73 if (content.contains("var show_1=\"0\"") || content.contains("show=\"0\"")) {
74 ipCameraHandler.setChannelState(CHANNEL_TEXT_OVERLAY, StringType.EMPTY);
76 value1 = Helper.searchString(content, "var name_1=\"");
77 if (!value1.isEmpty()) {
78 ipCameraHandler.setChannelState(CHANNEL_TEXT_OVERLAY, StringType.valueOf(value1));
80 value1 = Helper.searchString(content, "name=\"");
81 if (!value1.isEmpty()) {
82 ipCameraHandler.setChannelState(CHANNEL_TEXT_OVERLAY, StringType.valueOf(value1));
87 case "/cgi-bin/hi3510/param.cgi?cmd=getmdattr":// Motion Alarm old
88 if (content.contains("var m1_enable=\"1\"")) {
89 ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.ON);
91 ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.OFF);
94 case "/param.cgi?cmd=getalarmattr":// Motion Alarm new
95 if (content.contains("armed=\"1\"")) {
96 ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.ON);
98 ipCameraHandler.setChannelState(CHANNEL_ENABLE_MOTION_ALARM, OnOffType.OFF);
101 case "/param.cgi?cmd=getaudioalarmattr":// Audio Alarm
102 if (content.contains("enable=\"1\"")) {
103 ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.ON);
104 value1 = Helper.searchString(content, "aa_value=\"");
105 if (!value1.isEmpty()) {// old cameras have threshold in percentage
106 ipCameraHandler.setChannelState(CHANNEL_THRESHOLD_AUDIO_ALARM, PercentType.valueOf(value1));
108 value1 = Helper.searchString(content, "threshold=\"");
109 if (!value1.isEmpty()) {// newer cameras have values up to 10
110 ipCameraHandler.setChannelState(CHANNEL_THRESHOLD_AUDIO_ALARM,
111 PercentType.valueOf(value1 + "0"));
115 ipCameraHandler.setChannelState(CHANNEL_ENABLE_AUDIO_ALARM, OnOffType.OFF);
116 ipCameraHandler.setChannelState(CHANNEL_THRESHOLD_AUDIO_ALARM, OnOffType.OFF);
119 case "/param.cgi?cmd=getpirattr":// PIR Alarm
120 if (content.contains("enable=\"1\"")) {
121 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PIR_ALARM, OnOffType.ON);
123 ipCameraHandler.setChannelState(CHANNEL_ENABLE_PIR_ALARM, OnOffType.OFF);
125 // Reset the Alarm, need to find better place to put this.
126 ipCameraHandler.noMotionDetected(CHANNEL_PIR_ALARM);
128 case "/param.cgi?cmd=getioattr":// External Alarm Input
129 if (content.contains("enable=\"1\"")) {
130 ipCameraHandler.setChannelState(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT, OnOffType.ON);
132 ipCameraHandler.setChannelState(CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT, OnOffType.OFF);
136 if (requestUrl.startsWith("/param.cgi?cmd=setasaction&-server=1&enable=1")
137 && content.contains("response=\"200\";")) {// new
138 ipCameraHandler.newInstarApi = true;
139 ipCameraHandler.logger.debug("Alarm server successfully setup for a 2k+ Instar camera");
140 if (ipCameraHandler.cameraConfig.getFfmpegInput().isEmpty()) {
141 ipCameraHandler.rtspUri = "rtsp://" + ipCameraHandler.cameraConfig.getIp()
144 if (ipCameraHandler.cameraConfig.getMjpegUrl().isEmpty()) {
145 ipCameraHandler.mjpegUri = "/livestream/12?action=play&media=mjpeg";
147 if (ipCameraHandler.cameraConfig.getSnapshotUrl().isEmpty()) {
148 ipCameraHandler.snapshotUri = "/snap.cgi?chn=12";
150 } else if (requestUrl.startsWith("/param.cgi?cmd=setmdalarm&-aname=server2&-switch=on&-interval=1")
151 && content.startsWith("[Succeed]set ok")) {
152 ipCameraHandler.newInstarApi = false;
153 ipCameraHandler.logger.debug("Alarm server successfully setup for a 1080p Instar camera");
155 ipCameraHandler.logger.debug("Unknown reply from URI:{}", requestUrl);
159 ReferenceCountUtil.release(msg);
163 // This handles the commands that come from the openHAB event bus.
164 public void handleCommand(ChannelUID channelUID, Command command) {
165 if (command instanceof RefreshType) {
166 switch (channelUID.getId()) {
167 case CHANNEL_THRESHOLD_AUDIO_ALARM:
168 case CHANNEL_ENABLE_AUDIO_ALARM:
169 ipCameraHandler.sendHttpGET("/param.cgi?cmd=getaudioalarmattr");
171 case CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT:
172 ipCameraHandler.sendHttpGET("/param.cgi?cmd=getioattr");
174 case CHANNEL_ENABLE_MOTION_ALARM:
175 if (ipCameraHandler.newInstarApi) {
176 ipCameraHandler.sendHttpGET("/param.cgi?cmd=getalarmattr");
178 ipCameraHandler.sendHttpGET("/cgi-bin/hi3510/param.cgi?cmd=getmdattr");
181 case CHANNEL_ENABLE_PIR_ALARM:
182 ipCameraHandler.sendHttpGET("/param.cgi?cmd=getpirattr");
184 case CHANNEL_AUTO_LED:
185 ipCameraHandler.sendHttpGET("/param.cgi?cmd=getinfrared");
187 case CHANNEL_TEXT_OVERLAY:
188 ipCameraHandler.sendHttpGET("/param.cgi?cmd=getoverlayattr&-region=1");
192 } // end of "REFRESH"
193 if (ipCameraHandler.newInstarApi) {
194 switch (channelUID.getId()) {
195 case CHANNEL_THRESHOLD_AUDIO_ALARM:
196 if (OnOffType.OFF.equals(command) || PercentType.ZERO.equals(command)) {
197 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setaudioalarmattr&enable=0");
198 } else if (OnOffType.ON.equals(command)) {
199 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setaudioalarmattr&enable=1");
200 } else if (command instanceof PercentType) {
201 int value = ((PercentType) command).toBigDecimal().divide(BigDecimal.TEN).intValue();
202 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setaudioalarmattr&enable=1&threshold=" + value);
205 case CHANNEL_ENABLE_AUDIO_ALARM:
206 if (OnOffType.ON.equals(command)) {
207 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setaudioalarmattr&enable=1");
209 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setaudioalarmattr&enable=0");
212 case CHANNEL_ENABLE_MOTION_ALARM:
213 if (OnOffType.ON.equals(command)) {
214 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setalarmattr&armed=1");
216 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setalarmattr&armed=0");
219 case CHANNEL_TEXT_OVERLAY:
220 String text = Helper.encodeSpecialChars(command.toString());
221 if (text.isEmpty()) {
222 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setoverlayattr&-region=1&-show=0");
224 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=" + text);
227 case CHANNEL_AUTO_LED:
228 if (OnOffType.ON.equals(command)) {
229 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setinfrared&-infraredstat=2");
231 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setinfrared&-infraredstat=0");
234 case CHANNEL_ENABLE_PIR_ALARM:
235 if (OnOffType.ON.equals(command)) {
236 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setpirattr&enable=1");
238 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setpirattr&enable=0");
241 case CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT:
242 if (OnOffType.ON.equals(command)) {
243 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setioattr&enable=1");
245 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setioattr&enable=0");
250 switch (channelUID.getId()) {
251 case CHANNEL_THRESHOLD_AUDIO_ALARM:
252 if (OnOffType.OFF.equals(command) || PercentType.ZERO.equals(command)) {
253 ipCameraHandler.sendHttpGET("/cgi-bin/hi3510/param.cgi?cmd=setaudioalarmattr&-aa_enable=0");
254 } else if (OnOffType.ON.equals(command)) {
255 ipCameraHandler.sendHttpGET("/cgi-bin/hi3510/param.cgi?cmd=setaudioalarmattr&-aa_enable=1");
256 } else if (command instanceof PercentType) {
257 int value = ((PercentType) command).toBigDecimal().divide(BigDecimal.TEN).intValue();
258 ipCameraHandler.sendHttpGET(
259 "/cgi-bin/hi3510/param.cgi?cmd=setaudioalarmattr&-aa_enable=1&-aa_value=" + value * 10);
262 case CHANNEL_ENABLE_AUDIO_ALARM:
263 if (OnOffType.ON.equals(command)) {
264 ipCameraHandler.sendHttpGET("/cgi-bin/hi3510/param.cgi?cmd=setaudioalarmattr&-aa_enable=1");
266 ipCameraHandler.sendHttpGET("/cgi-bin/hi3510/param.cgi?cmd=setaudioalarmattr&-aa_enable=0");
269 case CHANNEL_ENABLE_MOTION_ALARM:
270 if (OnOffType.ON.equals(command)) {
271 ipCameraHandler.sendHttpGET(
272 "/cgi-bin/hi3510/param.cgi?cmd=setmdattr&-enable=1&-name=1&cmd=setmdattr&-enable=1&-name=2&cmd=setmdattr&-enable=1&-name=3&cmd=setmdattr&-enable=1&-name=4");
274 ipCameraHandler.sendHttpGET(
275 "/cgi-bin/hi3510/param.cgi?cmd=setmdattr&-enable=0&-name=1&cmd=setmdattr&-enable=0&-name=2&cmd=setmdattr&-enable=0&-name=3&cmd=setmdattr&-enable=0&-name=4");
278 case CHANNEL_TEXT_OVERLAY:
279 String text = Helper.encodeSpecialChars(command.toString());
280 if (text.isEmpty()) {
281 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setoverlayattr&-region=1&-show=0");
283 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=" + text);
286 case CHANNEL_AUTO_LED:
287 if (OnOffType.ON.equals(command)) {
288 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setinfrared&-infraredstat=auto");
290 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setinfrared&-infraredstat=close");
293 case CHANNEL_ENABLE_PIR_ALARM:
294 if (OnOffType.ON.equals(command)) {
295 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setpirattr&-pir_enable=1");
297 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setpirattr&-pir_enable=0");
300 case CHANNEL_ENABLE_EXTERNAL_ALARM_INPUT:
301 if (OnOffType.ON.equals(command)) {
302 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setioattr&-io_enable=1");
304 ipCameraHandler.sendHttpGET("/param.cgi?cmd=setioattr&-io_enable=0");
311 public void alarmTriggered(String alarm) {
312 // older cameras placed the & for the first query, whilst newer cameras do not.
313 // examples are /instar?&active=6 vs /instar?active=6&object=0
314 ipCameraHandler.setChannelState(CHANNEL_LAST_EVENT_DATA, new StringType(alarm));
315 String alarmCode = alarm.replaceAll(".+active=", "");
316 alarmCode = alarmCode.replaceAll("&.+", "");
317 String objectCode = alarm.replaceAll(".+object=", "");
319 case "1":// The motion area boxes 1-4
323 ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
326 ipCameraHandler.motionDetected(CHANNEL_PIR_ALARM);
328 case "6":// Audio Alarm
329 ipCameraHandler.audioDetected();
331 case "7":// Motion area 1 + PIR
332 case "8":// Motion area 2 + PIR
333 case "9":// Motion area 3 + PIR
334 case "10":// Motion area 4 + PIR
335 ipCameraHandler.motionDetected(CHANNEL_MOTION_ALARM);
336 ipCameraHandler.motionDetected(CHANNEL_PIR_ALARM);
339 ipCameraHandler.logger.debug("Unknown alarm code:{}", alarmCode);
341 if (!objectCode.isEmpty()) {
342 switch (objectCode) {
343 // person=1, car=2, animal=4 so 1+2+4=7 means one of each.
344 case "0":// no object
347 ipCameraHandler.motionDetected(CHANNEL_HUMAN_ALARM);
350 ipCameraHandler.motionDetected(CHANNEL_CAR_ALARM);
353 ipCameraHandler.motionDetected(CHANNEL_HUMAN_ALARM);
354 ipCameraHandler.motionDetected(CHANNEL_CAR_ALARM);
357 ipCameraHandler.motionDetected(CHANNEL_ANIMAL_ALARM);
360 ipCameraHandler.motionDetected(CHANNEL_HUMAN_ALARM);
361 ipCameraHandler.motionDetected(CHANNEL_ANIMAL_ALARM);
364 ipCameraHandler.motionDetected(CHANNEL_CAR_ALARM);
365 ipCameraHandler.motionDetected(CHANNEL_ANIMAL_ALARM);
368 ipCameraHandler.motionDetected(CHANNEL_HUMAN_ALARM);
369 ipCameraHandler.motionDetected(CHANNEL_CAR_ALARM);
370 ipCameraHandler.motionDetected(CHANNEL_ANIMAL_ALARM);
373 if (objectCode.startsWith("/instar?")) {
374 return;// has no object due to older Instar camera model
376 ipCameraHandler.logger.debug("Unknown object detection code:{}", objectCode);
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 ArrayList<String> getLowPriorityRequests() {
384 ArrayList<String> lowPriorityRequests = new ArrayList<String>(7);
385 lowPriorityRequests.add("/param.cgi?cmd=getaudioalarmattr");
386 lowPriorityRequests.add("/cgi-bin/hi3510/param.cgi?cmd=getmdattr");
387 lowPriorityRequests.add("/param.cgi?cmd=getalarmattr");
388 lowPriorityRequests.add("/param.cgi?cmd=getinfrared");
389 lowPriorityRequests.add("/param.cgi?cmd=getoverlayattr&-region=1");
390 lowPriorityRequests.add("/param.cgi?cmd=getpirattr");
391 lowPriorityRequests.add("/param.cgi?cmd=getioattr"); // ext alarm input on/off
392 return lowPriorityRequests;