49_IPCAM.pm 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. # $Id: 49_IPCAM.pm 2626 2013-02-01 19:19:15Z mfr69bs $
  2. # vim: ts=2:et
  3. ################################################################
  4. #
  5. # (c) 2012 Copyright: Martin Fischer (m_fischer at gmx dot de)
  6. # All rights reserved
  7. #
  8. # This script is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # The GNU General Public License can be found at
  14. # http://www.gnu.org/copyleft/gpl.html.
  15. # A copy is found in the textfile GPL.txt and important notices to the license
  16. # from the author is found in LICENSE.txt distributed with these scripts.
  17. #
  18. # This script is distributed in the hope that it will be useful,
  19. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. # GNU General Public License for more details.
  22. #
  23. ################################################################
  24. package main;
  25. use strict;
  26. use warnings;
  27. sub IPCAM_getSnapshot($);
  28. sub IPCAM_guessFileFormat($);
  29. my %gets = (
  30. "image" => "",
  31. "last" => "",
  32. "snapshots" => "",
  33. );
  34. my %sets = (
  35. "cmd" => "",
  36. "pan" => "left,right",
  37. "pos" => "",
  38. "tilt" => "up,down",
  39. "raw" => "",
  40. );
  41. #####################################
  42. sub
  43. IPCAM_Initialize($$)
  44. {
  45. my ($hash) = @_;
  46. $hash->{DefFn} = "IPCAM_Define";
  47. $hash->{UndefFn} = "IPCAM_Undef";
  48. $hash->{GetFn} = "IPCAM_Get";
  49. $hash->{SetFn} = "IPCAM_Set";
  50. $hash->{AttrList} = "basicauth delay credentials path pathCmd pathPanTilt query snapshots storage timestamp:0,1 ".
  51. "cmdPanLeft cmdPanRight cmdTiltUp cmdTiltDown cmdStep ".
  52. "cmdPos01 cmdPos02 cmdPos03 cmdPos04 cmdPos05 cmdPos06 cmdPos07 cmdPos08 ".
  53. "cmdPos09 cmdPos10 cmdPos11 cmdPos12 cmdPos13 cmdPos14 cmdPos15 cmdPosHome ".
  54. "cmd01 cmd02 cmd03 cmd04 cmd05 cmd06 cmd07 cmd08 ".
  55. "cmd09 cmd10 cmd11 cmd12 cmd13 cmd14 cmd15 ".
  56. "do_not_notify:1,0 showtime:1,0 ".
  57. "loglevel:0,1,2,3,4,5,6 disable:0,1 ".
  58. $readingFnAttributes;
  59. }
  60. #####################################
  61. sub
  62. IPCAM_Define($$) {
  63. my ($hash, $def) = @_;
  64. # define <name> IPCAM <camip:port>
  65. # define webcam IPCAM 192.168.1.58:81
  66. my @a = split("[ \t][ \t]*", $def);
  67. return "Wrong syntax: use 'define <name> IPCAM <camip:port>'"
  68. if(@a != 3);
  69. my $name = $a[0];
  70. my $auth = $a[2];
  71. $hash->{AUTHORITY} = $auth;
  72. $hash->{STATE} = "Defined";
  73. $hash->{SEQ} = 0;
  74. return undef;
  75. }
  76. #####################################
  77. sub
  78. IPCAM_Undef($$) {
  79. my ($hash, $name) = @_;
  80. delete($modules{IPCAM}{defptr}{$hash->{NAME}});
  81. RemoveInternalTimer($hash);
  82. return undef;
  83. }
  84. #####################################
  85. sub
  86. IPCAM_Set($@) {
  87. my ($hash, @a) = @_;
  88. my $name = $hash->{NAME};
  89. my @camCmd;
  90. # check argument
  91. return "Unknown argument $a[1], choose one of ".join(" ", sort keys %sets)
  92. if(!defined($sets{$a[1]}));
  93. shift @a;
  94. my $cmd = $a[0];
  95. shift @a;
  96. my @args = @a;
  97. if($cmd eq "pan" || $cmd eq "tilt") {
  98. # check syntax
  99. return "argument is missing for $cmd"
  100. if(int(@args) < 1);
  101. return "Unknown argument $args[0], choose one of ".join(" ", split(",",$sets{$cmd}))
  102. if($sets{$cmd} !~ /$args[0]/);
  103. return "Command for '$cmd $args[0]' is not defined. Please add this attribute first: " .
  104. "'attr $name cmd".ucfirst($cmd).ucfirst($args[0])." <your_camera_command>'"
  105. if(!defined(AttrVal($name,"cmd".ucfirst($cmd).ucfirst($args[0]),undef)));
  106. return "Wrong argument $args[1], only one digit for a step size is allowed"
  107. if(defined($args[1]) && $args[1] !~ /\d+/);
  108. return "Command for 'step' is not defined. Please add this attribute first: " .
  109. "'attr $name cmdStep <your_camera_command>'"
  110. if(defined($args[1]) && !defined(AttrVal($name,"cmdStep",undef)));
  111. push(@camCmd,$attr{$name}{"cmd".ucfirst($cmd).ucfirst($args[0])});
  112. push(@camCmd,$attr{$name}{"cmdStep"}."=".$args[1])
  113. if(defined($args[1]));
  114. } elsif($cmd eq "pos") {
  115. # check syntax
  116. return "argument is missing for $cmd"
  117. if(int(@args) < 1);
  118. return "Wrong argument $args[0], only digits from 1 to 15 or home are allowed"
  119. if(defined($args[0]) && $args[0] !~ /^([1-9]|1[0-5])$/ && $args[0] ne "home");
  120. my $arg = ($args[0] =~ /\d+/) ? sprintf("cmdPos%02d",$args[0]) : "cmdPosHome";
  121. return "Command for '$cmd $args[0]' is not defined. Please add this attribute first: " .
  122. "'attr $name $arg <your_camera_command>'"
  123. if(!defined($attr{$name}{$arg}));
  124. push(@camCmd,$attr{$name}{$arg});
  125. } elsif($cmd eq "cmd") {
  126. # check syntax
  127. return "argument is missing for $cmd"
  128. if(int(@args) < 1);
  129. return "Wrong argument $args[0], only digits from 1 to 15 are allowed"
  130. if(defined($args[0]) && $args[0] !~ /^([1-9]|1[0-5])$/);
  131. my $arg = sprintf("cmd%02d",$args[0]);
  132. return "Command for '$cmd $args[0]' is not defined. Please add this attribute first: " .
  133. "'attr $name $arg <your_camera_command>'"
  134. if(!defined($attr{$name}{$arg}));
  135. push(@camCmd,$attr{$name}{$arg});
  136. } elsif($cmd eq "raw") {
  137. # check syntax
  138. return "argument is missing for $cmd"
  139. if(int(@args) < 1);
  140. my $arg = "@args";
  141. push(@camCmd,$arg);
  142. }
  143. if(@camCmd) {
  144. my $camAuth = $hash->{AUTHORITY};
  145. my $basicauth = (defined($attr{$name}{basicauth}) ? $attr{$name}{basicauth} : undef);
  146. my $camURI;
  147. my $camPath = (defined($attr{$name}{path}) ? $attr{$name}{path} : undef);
  148. my $camQuery = join("&",@camCmd);
  149. if(($cmd eq "pan" || $cmd eq "tilt" || $cmd =~ /pos/) &&
  150. defined($attr{$name}{pathPanTilt})) {
  151. $camPath = $attr{$name}{pathPanTilt};
  152. } elsif($cmd eq "cmd" && defined($attr{$name}{pathCmd})) {
  153. $camPath = $attr{$name}{pathCmd};
  154. } elsif($cmd eq "raw") {
  155. $camPath = $camQuery;
  156. } else {
  157. $camPath = $attr{$name}{path};
  158. }
  159. return "Missing a path value for camURI. Please set attribute 'path', 'pathCmd' and/or 'pathPanTilt' first."
  160. if(!$camPath && $cmd ne "raw");
  161. if($basicauth) {
  162. $camURI = "http://$basicauth" . "@" . "$camAuth/$camPath";
  163. } else {
  164. $camURI = "http://$camAuth/$camPath";
  165. }
  166. if($cmd eq "cmd" && defined($attr{$name}{pathCmd})) {
  167. $camURI .= "?$camQuery";
  168. } elsif($cmd ne "raw") {
  169. $camURI .= "&$camQuery";
  170. }
  171. if($camURI =~ m/{USERNAME}/ || $camURI =~ m/{PASSWORD}/) {
  172. if(defined($attr{$name}{credentials})) {
  173. if(!open(CFG, $attr{$name}{credentials})) {
  174. Log 1, "IPCAM $name Cannot open credentials file: $attr{$name}{credentials}";
  175. return undef;
  176. }
  177. my @cfg = <CFG>;
  178. close(CFG);
  179. my %credentials;
  180. eval join("", @cfg);
  181. $camURI =~ s/{USERNAME}/$credentials{$name}{username}/g;
  182. $camURI =~ s/{PASSWORD}/$credentials{$name}{password}/g;
  183. }
  184. }
  185. my $camret = GetFileFromURLQuiet($camURI);
  186. Log 5, "ipcam return:$camret";
  187. }
  188. return undef;
  189. }
  190. #####################################
  191. sub
  192. IPCAM_Get($@) {
  193. my ($hash, @a) = @_;
  194. my $modpath = $attr{global}{modpath};
  195. my $name = $hash->{NAME};
  196. my $seqImages;
  197. my $seqDelay;
  198. my $seqWait;
  199. my $storage = (defined($attr{$name}{storage}) ? $attr{$name}{storage} : "$modpath/www/snapshots");
  200. # check syntax
  201. return "argument is missing @a"
  202. if(int(@a) != 2);
  203. # check argument
  204. return "Unknown argument $a[1], choose one of ".join(" ", sort keys %gets)
  205. if(!defined($gets{$a[1]}));
  206. # check attributes
  207. return "Attribute 'path' is missing. Please add this attribute first!"
  208. if(!defined($attr{$name}) || (defined($attr{$name}) && !defined($attr{$name}{path})));
  209. return "Attribute 'path' is defined but empty."
  210. if(defined($attr{$name}{path}) && $attr{$name}{path} eq "");
  211. return "Attribute 'query' is defined but empty."
  212. if(defined($attr{$name}{query}) && $attr{$name}{query} eq "");
  213. # define default storage
  214. if(!defined($attr{$name}{storage}) || $attr{$name}{storage} eq "") {
  215. $attr{$name}{storage} = $storage;
  216. }
  217. if(! -d $storage) {
  218. my $ret = mkdir "$storage";
  219. if($ret == 0) {
  220. Log 1, "ipcam Error while creating: $storage: $!";
  221. return "Error while creating storagepath $storage: $!";
  222. }
  223. }
  224. # get argument
  225. my $arg = $a[1];
  226. if($arg eq "image") {
  227. $seqImages = int(defined($attr{$name}{snapshots}) ? $attr{$name}{snapshots} : 1);
  228. $seqDelay = int(defined($attr{$name}{delay}) ? $attr{$name}{delay} : 0);
  229. $seqWait = 0;
  230. # housekeeping after number of sequence has changed
  231. my $readings = $hash->{READINGS};
  232. foreach my $r (sort keys %{$readings}) {
  233. if($r =~ /snapshot\d+/) {
  234. my $n = $r;
  235. $n =~ s/snapshot//;
  236. delete $readings->{$r} if( $r =~ m/snapshot/ && int($n) > $seqImages);
  237. Log 5, "IPCAM $name remove old reading: $r";
  238. }
  239. }
  240. $hash->{READINGS}{snapshots}{VAL} = 0;
  241. for (my $i=0;$i<$seqImages;$i++) {
  242. InternalTimer(gettimeofday()+$seqWait, "IPCAM_getSnapshot", $hash, 0);
  243. $seqWait = $seqWait + $seqDelay;
  244. }
  245. return undef;
  246. } elsif(defined($hash->{READINGS}{$arg})) {
  247. if(defined($hash->{READINGS}{$arg}{VAL})) {
  248. return "$name $arg => $hash->{READINGS}{$arg}{VAL}";
  249. } else {
  250. return "$name $arg => undef";
  251. }
  252. }
  253. }
  254. #####################################
  255. sub
  256. IPCAM_getSnapshot($) {
  257. my ($hash) = @_;
  258. my $name = $hash->{NAME};
  259. my $camAuth = $hash->{AUTHORITY};
  260. my $camURI;
  261. my $camPath;
  262. my $camQuery;
  263. my $camCredentials;
  264. my $imageFile;
  265. my $imageFormat;
  266. my $lastSnapshot;
  267. my $snapshot;
  268. my $dateTime;
  269. my $modpath = $attr{global}{modpath};
  270. my $seq = int(defined($hash->{SEQ}) ? $hash->{SEQ} : 0);
  271. my $seqImages = int(defined($attr{$name}{snapshots}) ? $attr{$name}{snapshots} : 1);
  272. my $seqF;
  273. my $seqL = length($seqImages);
  274. my $storage = (defined($attr{$name}{storage}) ? $attr{$name}{storage} : "$modpath/www/snapshots");
  275. my $basicauth = (defined($attr{$name}{basicauth}) ? $attr{$name}{basicauth} : undef);
  276. my $timestamp;
  277. #if(!$storage) {
  278. # RemoveInternalTimer($hash);
  279. # return "Attribute 'storage' is missing. Please add this attribute first!";
  280. #}
  281. $camPath = $attr{$name}{path};
  282. $camQuery = $attr{$name}{query}
  283. if(defined($attr{$name}{query}) && $attr{$name}{query} ne "");
  284. if($basicauth) {
  285. $camURI = "http://$basicauth" . "@" . "$camAuth/$camPath";
  286. } else {
  287. $camURI = "http://$camAuth/$camPath";
  288. }
  289. $camURI .= "?$camQuery" if($camQuery);
  290. if($camURI =~ m/{USERNAME}/ || $camURI =~ m/{PASSWORD}/) {
  291. if(defined($attr{$name}{credentials})) {
  292. if(!open(CFG, $attr{$name}{credentials})) {
  293. Log 1, "IPCAM $name Cannot open credentials file: $attr{$name}{credentials}";
  294. RemoveInternalTimer($hash);
  295. return undef;
  296. }
  297. my @cfg = <CFG>;
  298. close(CFG);
  299. my %credentials;
  300. eval join("", @cfg);
  301. $camURI =~ s/{USERNAME}/$credentials{$name}{username}/;
  302. $camURI =~ s/{PASSWORD}/$credentials{$name}{password}/;
  303. }
  304. }
  305. $dateTime = TimeNow();
  306. $timestamp = $dateTime;
  307. $timestamp =~ s/ /_/g;
  308. $timestamp =~ s/(:|-)//g;
  309. $snapshot = GetFileFromURLQuiet($camURI);
  310. $imageFormat = IPCAM_guessFileFormat(\$snapshot);
  311. my @imageTypes = qw(JPEG PNG GIF TIFF BMP ICO PPM XPM XBM SVG);
  312. if( ! grep { $_ eq "$imageFormat"} @imageTypes) {
  313. Log 1, "IPCAM $name Wrong or not supported image format: $imageFormat";
  314. RemoveInternalTimer($hash);
  315. return undef;
  316. }
  317. Log GetLogLevel($name,5), "IPCAM $name Image Format: $imageFormat";
  318. readingsBeginUpdate($hash);
  319. if($seq < $seqImages) {
  320. $seq++;
  321. $seqF = sprintf("%0${seqL}d",$seq);
  322. $imageFormat = "JPG" if($imageFormat eq "JPEG");
  323. $lastSnapshot = $name."_snapshot.".lc($imageFormat);
  324. if(defined($attr{$name}{timestamp}) && $attr{$name}{timestamp} == 1) {
  325. $imageFile = $name."_".$timestamp.".".lc($imageFormat);
  326. } else {
  327. $imageFile = $name."_snapshot_".$seqF.".".lc($imageFormat);
  328. }
  329. if(!open(FH, ">$storage/$lastSnapshot")) {
  330. Log 1, "IPCAM $name Can't write $storage/$lastSnapshot: $!";
  331. RemoveInternalTimer($hash);
  332. readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1));
  333. return undef;
  334. }
  335. print FH $snapshot;
  336. close(FH);
  337. Log 5, "IPCAM $name snapshot $storage/$lastSnapshot written.";
  338. if(!open(FH, ">$storage/$imageFile")) {
  339. Log 1, "IPCAM $name Can't write $storage/$imageFile: $!";
  340. RemoveInternalTimer($hash);
  341. readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1));
  342. return undef;
  343. }
  344. print FH $snapshot;
  345. close(FH);
  346. Log 5, "IPCAM $name snapshot $storage/$imageFile written.";
  347. readingsBulkUpdate($hash,"last",$lastSnapshot);
  348. $hash->{STATE} = "last: $dateTime";
  349. $hash->{READINGS}{"snapshot$seqF"}{TIME} = $dateTime;
  350. $hash->{READINGS}{"snapshot$seqF"}{VAL} = $imageFile;
  351. }
  352. Log GetLogLevel($name,4), "IPCAM $name image: $imageFile";
  353. if($seq == $seqImages) {
  354. readingsBulkUpdate($hash,"snapshots",$seq);
  355. $seq = 0;
  356. }
  357. readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1));
  358. $hash->{SEQ} = $seq;
  359. return undef;
  360. }
  361. #####################################
  362. sub
  363. IPCAM_guessFileFormat($) {
  364. my ($src) = shift;
  365. my $header;
  366. my $srcHeader;
  367. open(my $s, "<", $src) || return "can't open source image: $!";
  368. $src = $s;
  369. my $reading = read($src, $srcHeader, 64);
  370. return "error while reading source image: $!" if(!$reading);
  371. local($_) = $srcHeader;
  372. return "JPEG" if /^\xFF\xD8/;
  373. return "PNG" if /^\x89PNG\x0d\x0a\x1a\x0a/;
  374. return "GIF" if /^GIF8[79]a/;
  375. return "TIFF" if /^MM\x00\x2a/;
  376. return "TIFF" if /^II\x2a\x00/;
  377. return "BMP" if /^BM/;
  378. return "ICO" if /^\000\000\001\000/;
  379. return "PPM" if /^P[1-6]/;
  380. return "XPM" if /(^\/\* XPM \*\/)|(static\s+char\s+\*\w+\[\]\s*=\s*{\s*"\d+)/;
  381. return "XBM" if /^(?:\/\*.*\*\/\n)?#define\s/;
  382. return "SVG" if /^(<\?xml|[\012\015\t ]*<svg\b)/;
  383. return "unknown";
  384. }
  385. # vim: ts=2:et
  386. 1;
  387. =pod
  388. =begin html
  389. <a name="IPCAM"></a>
  390. <h3>IPCAM</h3>
  391. <ul>
  392. <br>
  393. <a name"IPCAMdefine"></a>
  394. <strong>Define</strong>
  395. <ul>
  396. <code>define &lt;name&gt; IPCAM &lt;ip[:port]&gt;</code>
  397. <br>
  398. <br>
  399. Defines a network camera device to trigger snapshots on events.
  400. <br>
  401. <br>
  402. Network cameras (IP cameras) usually have a build-in function to create
  403. snapshot images. This module enables the event- or time-controlled
  404. recording of these images.
  405. <br>
  406. In addition, this module allows the recording of many image formats like
  407. JPEG, PNG, GIF, TIFF, BMP, ICO, PPM, XPM, XBM and SVG. The only requirement
  408. is that the recorded image must be accessible via a URL.
  409. <br>
  410. So it is also possible to record images of e.g. a public Weather Camera
  411. from the internet or any picture of a website.
  412. <br>
  413. Furthermore, it is possible to control the camera via PTZ-mode or custom commands.
  414. <br>
  415. <br>
  416. Examples:
  417. <br>
  418. <br>
  419. A local ip-cam takes 5 snapshots with 10 seconds delay per call:
  420. <br>
  421. <ul>
  422. <code>define ipcam IPCAM 192.168.1.205</code><br>
  423. <code>attr ipcam delay 10</code><br>
  424. <code>attr ipcam path snapshot.cgi?user=foo&amp;pwd=bar</code><br>
  425. <code>attr ipcam snapshots 5</code><br>
  426. <code>attr ipcam storage /srv/share/surveillance/snapshots</code><br>
  427. </ul>
  428. <br>
  429. A notify on a motion detection of a specified device:
  430. <br>
  431. <ul>
  432. <code>define MOTION.not.01 notify GH.ga.SEC.MD.01:.*on.* get ipcam image</code><br>
  433. </ul>
  434. <br>
  435. Send an eMail after snapshots are taken:
  436. <br>
  437. <ul>
  438. <code>define MOTION.not.02 notify ipcam:.*snapshots.* { myEmailFunction("%NAME") }</code><br>
  439. </ul>
  440. <br>
  441. A public web-cam takes only 1 snapshot per call:
  442. <br>
  443. <ul>
  444. <code>define schloss IPCAM www2.braunschweig.de</code><br>
  445. <code>attr schloss path webcam/schloss.jpg</code><br>
  446. <code>attr schloss storage /srv/share/surveillance/snapshots</code><br>
  447. </ul>
  448. <br>
  449. An at-Job takes every hour a snapshot:
  450. <br>
  451. <ul>
  452. <code>define snapshot_schloss at +*00:01:00 get schloss image</code><br>
  453. </ul>
  454. <br>
  455. Move the camera up:
  456. <br>
  457. <ul>
  458. <code>set ipcam tilt up</code>
  459. </ul>
  460. <br>
  461. Move the camera to a the predefined position 4:
  462. <br>
  463. <ul>
  464. <code>set ipcam pos 4</code>
  465. </ul>
  466. </ul>
  467. <br>
  468. <br>
  469. <a name="IPCAMset"></a>
  470. <strong>Set</strong>
  471. <ul>
  472. <code>set &lt;name&gt; &lt;value&gt; &lt;argument&gt;</code>
  473. <br>
  474. <br>
  475. where <code>value</code> is one of:
  476. <br>
  477. <ul>
  478. <li><code>cmd 1 .. 15</code><br>
  479. Sets the camera to a custom defined command. The command must be defined as an
  480. attribute first.
  481. <br>
  482. You can define up to 15 custom commands. The given number always relates to an
  483. equivalent attribute <code>cmd&lt;number&gt;</code>.
  484. </li>
  485. <li><code>pan &lt;direction&gt; [steps]</code><br>
  486. Move the camera to the given <code>&lt;direction&gt;</code>, where <code>&lt;direction&gt;</code>
  487. could be <code>left</code> or <code>right</code>.
  488. <br>
  489. The command always relates to an equivalent attribute <code>cmdPan&lt;direction&gt;</code>.
  490. <br>
  491. Furthermore, a step size can be specified, which relates to the equivalent attribute
  492. <code>cmdStep</code>.
  493. </li>
  494. <li><code>pos 1 .. 15|home</code><br>
  495. Sets the camera to a custom defined position in PTZ mode. The position must be
  496. defined as an attribute first.
  497. <br>
  498. You can define up to 15 custom positions and a predefined home position. The given
  499. number always relates to an equivalent attribute <code>cmdPos&lt;number&gt;</code>.
  500. </li>
  501. <li><code>tilt &lt;direction&gt; [steps]</code><br>
  502. Move the camera to the given <code>&lt;direction&gt;</code>, where <code>&lt;direction&gt;</code>
  503. could be <code>up</code> or <code>down</code>.
  504. <br>
  505. The command always relates to an equivalent attribute <code>cmdPan&lt;direction&gt;</code>.
  506. <br>
  507. Furthermore, a step size can be specified, which relates to the equivalent attribute
  508. <code>cmdStep</code>.
  509. </li>
  510. <li><code>raw &lt;argument&gt;</code><br>
  511. Sets the camera to a custom defined <code>argument</code>.
  512. </li>
  513. </ul>
  514. </ul>
  515. <br>
  516. <br>
  517. <a name="IPCAMget"></a>
  518. <strong>Get</strong>
  519. <ul>
  520. <code>get &lt;name&gt; &lt;value&gt;</code>
  521. <br>
  522. <br>
  523. where <code>value</code> is one of:
  524. <br>
  525. <ul>
  526. <li><code>image</code><br>
  527. Get one or more images of the defined IP-Cam. The number of images<br>
  528. and the time interval between images can be specified using the<br>
  529. attributes <code>snapshots</code> and <code>delay</code>.
  530. </li>
  531. <li><code>last</code><br>
  532. Show the name of the last snapshot.
  533. </li>
  534. <li><code>snapshots</code><br>
  535. Show the total number of a image sequence.
  536. </li>
  537. </ul>
  538. </ul>
  539. <br>
  540. <a name="IPCAMattr"></a>
  541. <strong>Attributes</strong>
  542. <ul>
  543. <li>
  544. basicauth<br>
  545. If your camera supports authentication like <code>http://username:password@domain.com/</code>, you
  546. can store your creditials within the <code>basicauth</code> attribute.<br>
  547. If you prefer to store the credentials in a file (take a look at the attribute <code>credentials</code>)
  548. you have to set the placeholder <code>{USERNAME}</code> and <code>{PASSWORD}</code> in the basicauth string.
  549. These placeholders will be replaced with the values from the credentials file.<br>
  550. Example:<br> <code>attr ipcam3 basicauth {USERNAME}:{PASSWORD}</code>
  551. </li>
  552. <li>
  553. cmd01, cmd02, cmd03, .. cmd13, cdm14, cdm15<br>
  554. It is possible to define up to 15 custom commands.<br>
  555. Examples:<br>
  556. <code>attr ipcam cmd01 led_mode=0</code><br>
  557. <code>attr ipcam cmd02 resolution=8</code><br>
  558. </li>
  559. <li>
  560. cmdPanLeft, cmdPanRight, cmdTiltUp, cmdTiltDown, cmdStep<br>
  561. Depending of the camera model, are different commands necessary.<br>
  562. Examples:<br>
  563. <code>attr ipcam cmdTiltUp command=0</code><br>
  564. <code>attr ipcam cmdTiltDown command=2</code><br>
  565. <code>attr ipcam cmdPanLeft command=4</code><br>
  566. <code>attr ipcam cmdPanRight command=6</code><br>
  567. <code>attr ipcam cmdStep onstep</code><br>
  568. </li>
  569. <li>
  570. cmdPos01, cmdPos02, cmdPos03, .. cmdPos13, cmdPos14, cmdPos15, cmdPosHome
  571. It is possible to define up to 15 predefined position in PTZ-mode.<br>
  572. Examples:<br>
  573. <code>attr ipcam cmdPosHome command=25</code><br>
  574. <code>attr ipcam cmdPos01 command=31</code><br>
  575. <code>attr ipcam cmdPos02 command=33</code><br>
  576. </li>
  577. <li>
  578. credentials<br>
  579. Defines the location of the credentials file.<br>
  580. If you prefer to store your cam credentials in a file instead be a part of the
  581. URI (see attributes <code>path</code> and <code>query</code>), set the full path
  582. with filename on this attribute.<br>
  583. Example:<br>
  584. <code>attr ipcam3 credentials /etc/fhem/ipcam.conf</code><br><br>
  585. The credentials file has the following structure:<br>
  586. <pre>
  587. #
  588. # Webcam credentials
  589. #
  590. $credentials{&lt;name_cam1&gt;}{username} = "&lt;your_username&gt;";
  591. $credentials{&lt;name_cam1&gt;}{password} = "&lt;your_password&gt;";
  592. $credentials{&lt;name_cam2&gt;}{username} = "&lt;your_username&gt;";
  593. $credentials{&lt;name_cam2&gt;}{password} = "&lt;your_password&gt;";
  594. ...
  595. </pre>
  596. Replace <code>&lt;name_cam1&gt;</code> respectively <code>&lt;name_cam2&gt;</code>
  597. with the names of your defined ip-cams and <code>&lt;your_username&gt;</code> respectively
  598. <code>&lt;your_password&gt;</code> with your credentials (all without the brackets
  599. <code>&lt;</code> and <code>&gt;</code>!).
  600. </li>
  601. <li>
  602. delay<br>
  603. Defines the time interval between snapshots in seconds.<br>
  604. If more then one snapshot is taken, then it makes sense to define a short delay
  605. between the snapshots. On the one hand, the camera is not addressed in short intervals
  606. and the second may be better represented movements between images.<br>
  607. Example: <code>attr ipcam3 delay 10</code>
  608. </li>
  609. <li><a href="#disable">disable</a></li>
  610. <li><a href="#do_not_notify">do_not_notify</a></li>
  611. <li><a href="#loglevel">loglevel</a></li>
  612. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  613. <li>
  614. path<br>
  615. Defines the path and query component of the complete <a href="http://de.wikipedia.org/wiki/Uniform_Resource_Identifier" target="_blank">URI</a> to get a snapshot of the
  616. camera. Is the full URI of your ip-cam for example <code>http://CAMERA_IP/snapshot.cgi?user=admin&amp;pwd=password</code>,
  617. then only the path and query part is specified here (without the leading slash (/).<br>
  618. Example:<br>
  619. <code>attr ipcam3 path snapshot.cgi?user=admin&amp;pwd=password</code><br><br>
  620. If you prefer to store the credentials in a file (take a look at the attribute <code>credentials</code>)
  621. you have to set the placeholder <code>{USERNAME}</code> and <code>{PASSWORD}</code> in the path string. These placeholders
  622. will be replaced with the values from the credentials file.<br>
  623. Example:<br>
  624. <code>attr ipcam3 path snapshot.cgi?user={USERNAME}&amp;pwd={PASSWORD}</code>
  625. </li>
  626. <li>
  627. pathCmd<br>
  628. Defines a path for the custom commands, if it is necessary.<br>
  629. Example:<br>
  630. <code>attr ipcam3 pathCmd set_misc.cgi</code>
  631. </li>
  632. <li>
  633. pathPanTilt<br>
  634. Defines a path for the PTZ-mode commands <code>pan</code>, <code>tilt</code> and <code>pos</code>,
  635. if it is necessary.<br>
  636. Example:<br>
  637. <code>attr ipcam3 pathPanTilt decoder_control.cgi?user={USERNAME}&amp;pwd={PASSWORD}</code>
  638. </li>
  639. <li><a href="#showtime">showtime</a></li>
  640. <li>
  641. snapshots<br>
  642. Defines the total number of snapshots to be taken with the <code>get &lt;name&gt; image</code> command.
  643. If this attribute is not defined, then the default value is 1.<br>
  644. The snapshots are stored in the given path of the attribute <code>storage</code> and are
  645. numbered sequentially (starts with 1) like <code>snapshot_01</code>, <code>snapshot_02</code>, etc.
  646. Furthermore, an additional file <code>last</code> will be saved, which is identical with
  647. the last snapshot-image. The module checks the imagetype and stores all these files with
  648. the devicename and a correct extension, e.g. <code>&lt;devicename&gt;_snapshot_01.jpg</code>.<br>
  649. If you like a timestamp instead a sequentially number, take a look at the attribute <code>timestamp</code>.<br>
  650. All files are overwritten on every <code>get &lt;name&gt; image</code> command (except: snapshots
  651. with a timestamp. So, keep an eye on your diskspace if you use a timestamp extension!).<br>
  652. Example:<br>
  653. <code>attr ipcam3 snapshots 5</code>
  654. </li>
  655. <li>
  656. storage<br>
  657. Defines the location for the file storage of the snapshots.<br>
  658. Default: <code>$modpath/www/snapshots</code><br>
  659. Example:<br>
  660. <code>attr ipcam3 storage /srv/share/surveillance/snapshots</code>
  661. </li>
  662. <li>
  663. timestamp<br>
  664. If this attribute is unset or set to 0, snapshots are stored with a sequentially number
  665. like <code>&lt;devicename&gt;_snapshot_01.jpg</code>, <code>&lt;devicename&gt;_snapshot_02.jpg</code>, etc.<br>
  666. If you like filenames with a timestamp postfix, e.g. <code>&lt;devicename&gt;_20121023_002602.jpg</code>,
  667. set this attribute to 1.
  668. </li>
  669. </ul>
  670. <br>
  671. <a name="IPCAMevents"></a>
  672. <strong>Generated events</strong>
  673. <ul>
  674. <li>last: &lt;name_of_device&gt;_snapshot.&lt;image_extension&gt;</li>
  675. <li>snapshots: &lt;total_number_of_taken_snapshots_at_end&gt;</li>
  676. </ul>
  677. <br>
  678. </ul>
  679. =end html
  680. =cut