From 3e6460c3dfa685409c7beadac148e400b62eaa82 Mon Sep 17 00:00:00 2001 From: mfr69bs <> Date: Fri, 1 Feb 2013 19:19:15 +0000 Subject: [PATCH] added new features and basicauth to 49_IPCAM.pm, added basicauth support to HttpUtils.pm git-svn-id: https://svn.fhem.de/fhem/trunk@2626 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/49_IPCAM.pm | 381 +++++++++++++++++++++++++++++++++++------ fhem/FHEM/HttpUtils.pm | 14 +- 3 files changed, 344 insertions(+), 52 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 69e01f485..49a34fa22 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -65,6 +65,7 @@ - change: update supports the display and confirmation of system messages via the new notice command (M. Fischer) - change: BS, USF1000, dummy migrated to readingsFN (Boris) + - feature: added new set commands and basicauth to 49_IPCAM.pm (M. Fischer) - 2012-10-28 (5.3) diff --git a/fhem/FHEM/49_IPCAM.pm b/fhem/FHEM/49_IPCAM.pm index aa2d5ba47..a9e6f2dc4 100644 --- a/fhem/FHEM/49_IPCAM.pm +++ b/fhem/FHEM/49_IPCAM.pm @@ -1,4 +1,5 @@ # $Id$ +# vim: ts=2:et ################################################################ # # (c) 2012 Copyright: Martin Fischer (m_fischer at gmx dot de) @@ -34,6 +35,14 @@ my %gets = ( "snapshots" => "", ); +my %sets = ( + "cmd" => "", + "pan" => "left,right", + "pos" => "", + "tilt" => "up,down", + "raw" => "", +); + ##################################### sub IPCAM_Initialize($$) @@ -43,7 +52,13 @@ IPCAM_Initialize($$) $hash->{DefFn} = "IPCAM_Define"; $hash->{UndefFn} = "IPCAM_Undef"; $hash->{GetFn} = "IPCAM_Get"; - $hash->{AttrList} = "delay credentials path query snapshots storage timestamp:0,1 ". + $hash->{SetFn} = "IPCAM_Set"; + $hash->{AttrList} = "basicauth delay credentials path pathCmd pathPanTilt query snapshots storage timestamp:0,1 ". + "cmdPanLeft cmdPanRight cmdTiltUp cmdTiltDown cmdStep ". + "cmdPos01 cmdPos02 cmdPos03 cmdPos04 cmdPos05 cmdPos06 cmdPos07 cmdPos08 ". + "cmdPos09 cmdPos10 cmdPos11 cmdPos12 cmdPos13 cmdPos14 cmdPos15 cmdPosHome ". + "cmd01 cmd02 cmd03 cmd04 cmd05 cmd06 cmd07 cmd08 ". + "cmd09 cmd10 cmd11 cmd12 cmd13 cmd14 cmd15 ". "do_not_notify:1,0 showtime:1,0 ". "loglevel:0,1,2,3,4,5,6 disable:0,1 ". $readingFnAttributes; @@ -83,6 +98,146 @@ IPCAM_Undef($$) { return undef; } +##################################### +sub +IPCAM_Set($@) { + my ($hash, @a) = @_; + my $name = $hash->{NAME}; + my @camCmd; + + # check argument + return "Unknown argument $a[1], choose one of ".join(" ", sort keys %sets) + if(!defined($sets{$a[1]})); + + shift @a; + my $cmd = $a[0]; + shift @a; + my @args = @a; + + if($cmd eq "pan" || $cmd eq "tilt") { + + # check syntax + return "argument is missing for $cmd" + if(int(@args) < 1); + + return "Unknown argument $args[0], choose one of ".join(" ", split(",",$sets{$cmd})) + if($sets{$cmd} !~ /$args[0]/); + + return "Command for '$cmd $args[0]' is not defined. Please add this attribute first: " . + "'attr $name cmd".ucfirst($cmd).ucfirst($args[0])." '" + if(!defined(AttrVal($name,"cmd".ucfirst($cmd).ucfirst($args[0]),undef))); + + return "Wrong argument $args[1], only one digit for a step size is allowed" + if(defined($args[1]) && $args[1] !~ /\d+/); + + return "Command for 'step' is not defined. Please add this attribute first: " . + "'attr $name cmdStep '" + if(defined($args[1]) && !defined(AttrVal($name,"cmdStep",undef))); + + push(@camCmd,$attr{$name}{"cmd".ucfirst($cmd).ucfirst($args[0])}); + push(@camCmd,$attr{$name}{"cmdStep"}."=".$args[1]) + if(defined($args[1])); + + } elsif($cmd eq "pos") { + + # check syntax + return "argument is missing for $cmd" + if(int(@args) < 1); + + return "Wrong argument $args[0], only digits from 1 to 15 or home are allowed" + if(defined($args[0]) && $args[0] !~ /^([1-9]|1[0-5])$/ && $args[0] ne "home"); + + my $arg = ($args[0] =~ /\d+/) ? sprintf("cmdPos%02d",$args[0]) : "cmdPosHome"; + return "Command for '$cmd $args[0]' is not defined. Please add this attribute first: " . + "'attr $name $arg '" + if(!defined($attr{$name}{$arg})); + + push(@camCmd,$attr{$name}{$arg}); + + } elsif($cmd eq "cmd") { + + # check syntax + return "argument is missing for $cmd" + if(int(@args) < 1); + + return "Wrong argument $args[0], only digits from 1 to 15 are allowed" + if(defined($args[0]) && $args[0] !~ /^([1-9]|1[0-5])$/); + + my $arg = sprintf("cmd%02d",$args[0]); + return "Command for '$cmd $args[0]' is not defined. Please add this attribute first: " . + "'attr $name $arg '" + if(!defined($attr{$name}{$arg})); + + push(@camCmd,$attr{$name}{$arg}); + + } elsif($cmd eq "raw") { + + # check syntax + return "argument is missing for $cmd" + if(int(@args) < 1); + + my $arg = "@args"; + push(@camCmd,$arg); + + } + + if(@camCmd) { + my $camAuth = $hash->{AUTHORITY}; + my $basicauth = (defined($attr{$name}{basicauth}) ? $attr{$name}{basicauth} : undef); + my $camURI; + my $camPath = (defined($attr{$name}{path}) ? $attr{$name}{path} : undef); + my $camQuery = join("&",@camCmd); + + if(($cmd eq "pan" || $cmd eq "tilt" || $cmd =~ /pos/) && + defined($attr{$name}{pathPanTilt})) { + $camPath = $attr{$name}{pathPanTilt}; + } elsif($cmd eq "cmd" && defined($attr{$name}{pathCmd})) { + $camPath = $attr{$name}{pathCmd}; + } elsif($cmd eq "raw") { + $camPath = $camQuery; + } else { + $camPath = $attr{$name}{path}; + } + + return "Missing a path value for camURI. Please set attribute 'path', 'pathCmd' and/or 'pathPanTilt' first." + if(!$camPath && $cmd ne "raw"); + + if($basicauth) { + $camURI = "http://$basicauth" . "@" . "$camAuth/$camPath"; + } else { + $camURI = "http://$camAuth/$camPath"; + } + + if($cmd eq "cmd" && defined($attr{$name}{pathCmd})) { + $camURI .= "?$camQuery"; + } elsif($cmd ne "raw") { + $camURI .= "&$camQuery"; + } + + if($camURI =~ m/{USERNAME}/ || $camURI =~ m/{PASSWORD}/) { + + if(defined($attr{$name}{credentials})) { + if(!open(CFG, $attr{$name}{credentials})) { + Log 1, "IPCAM $name Cannot open credentials file: $attr{$name}{credentials}"; + return undef; + } + my @cfg = ; + close(CFG); + my %credentials; + eval join("", @cfg); + $camURI =~ s/{USERNAME}/$credentials{$name}{username}/g; + $camURI =~ s/{PASSWORD}/$credentials{$name}{password}/g; + } + } + + my $camret = GetFileFromURLQuiet($camURI); + Log 5, "ipcam return:$camret"; + + } + + return undef; +} + ##################################### sub IPCAM_Get($@) { @@ -181,6 +336,7 @@ IPCAM_getSnapshot($) { my $seqF; my $seqL = length($seqImages); my $storage = (defined($attr{$name}{storage}) ? $attr{$name}{storage} : "$modpath/www/snapshots"); + my $basicauth = (defined($attr{$name}{basicauth}) ? $attr{$name}{basicauth} : undef); my $timestamp; #if(!$storage) { @@ -192,7 +348,11 @@ IPCAM_getSnapshot($) { $camQuery = $attr{$name}{query} if(defined($attr{$name}{query}) && $attr{$name}{query} ne ""); - $camURI = "http://$camAuth/$camPath"; + if($basicauth) { + $camURI = "http://$basicauth" . "@" . "$camAuth/$camPath"; + } else { + $camURI = "http://$camAuth/$camPath"; + } $camURI .= "?$camQuery" if($camQuery); if($camURI =~ m/{USERNAME}/ || $camURI =~ m/{PASSWORD}/) { @@ -320,77 +480,147 @@ IPCAM_guessFileFormat($) {
- Define + Define
    define <name> IPCAM <ip[:port]> -

    - - Defines a network camera device to trigger snapshots on events.

    - +
    +
    + Defines a network camera device to trigger snapshots on events. +
    +
    Network cameras (IP cameras) usually have a build-in function to create snapshot images. This module enables the event- or time-controlled - recording of these images.
    + recording of these images. +
    In addition, this module allows the recording of many image formats like JPEG, PNG, GIF, TIFF, BMP, ICO, PPM, XPM, XBM and SVG. The only requirement - is that the recorded image must be accessible via a URL.
    + is that the recorded image must be accessible via a URL. +
    So it is also possible to record images of e.g. a public Weather Camera - from the internet or any picture of a website.

    - - Examples:

    - A local ip-cam takes 5 snapshots with 10 seconds delay per call:
    + from the internet or any picture of a website. +
    + Furthermore, it is possible to control the camera via PTZ-mode or custom commands. +
    +
    + Examples: +
    +
    + A local ip-cam takes 5 snapshots with 10 seconds delay per call: +
      define ipcam IPCAM 192.168.1.205
      attr ipcam delay 10
      attr ipcam path snapshot.cgi?user=foo&pwd=bar
      attr ipcam snapshots 5
      attr ipcam storage /srv/share/surveillance/snapshots
      -

    - - A notify on a motion detection of a specified device:
    +
+
+ A notify on a motion detection of a specified device: +
    define MOTION.not.01 notify GH.ga.SEC.MD.01:.*on.* get ipcam image
    -

- - Send an eMail after snapshots are taken:
+ +
+ Send an eMail after snapshots are taken: +
    define MOTION.not.02 notify ipcam:.*snapshots.* { myEmailFunction("%NAME") }
    -

- - A public web-cam takes only 1 snapshot per call:
+ +
+ A public web-cam takes only 1 snapshot per call: +
    define schloss IPCAM www2.braunschweig.de
    attr schloss path webcam/schloss.jpg
    attr schloss storage /srv/share/surveillance/snapshots
    -

- - An at-Job takes every hour a snapshot:
+ +
+ An at-Job takes every hour a snapshot: +
    define snapshot_schloss at +*00:01:00 get schloss image
    -

- + +
+ Move the camera up: +
+
    + set ipcam tilt up +
+
+ Move the camera to a the predefined position 4: +
+
    + set ipcam pos 4 +
- - Set
    N/A

- +
+
+ + Set +
    + set <name> <value> <argument> +
    +
    + where value is one of: +
    +
      +
    • cmd 1 .. 15
      + Sets the camera to a custom defined command. The command must be defined as an + attribute first. +
      + You can define up to 15 custom commands. The given number always relates to an + equivalent attribute cmd<number>. +
    • +
    • pan <direction> [steps]
      + Move the camera to the given <direction>, where <direction> + could be left or right. +
      + The command always relates to an equivalent attribute cmdPan<direction>. +
      + Furthermore, a step size can be specified, which relates to the equivalent attribute + cmdStep. +
    • +
    • pos 1 .. 15|home
      + Sets the camera to a custom defined position in PTZ mode. The position must be + defined as an attribute first. +
      + You can define up to 15 custom positions and a predefined home position. The given + number always relates to an equivalent attribute cmdPos<number>. +
    • +
    • tilt <direction> [steps]
      + Move the camera to the given <direction>, where <direction> + could be up or down. +
      + The command always relates to an equivalent attribute cmdPan<direction>. +
      + Furthermore, a step size can be specified, which relates to the equivalent attribute + cmdStep. +
    • +
    • raw <argument>
      + Sets the camera to a custom defined argument. +
    • +
    +
+
+
- Get + Get
    get <name> <value> -

    - where value is one of:
    +
    +
    + where value is one of: +
      -
    • - image
      +
    • image
      Get one or more images of the defined IP-Cam. The number of images
      and the time interval between images can be specified using the
      attributes snapshots and delay.
    • -
    • - last
      +
    • last
      Show the name of the last snapshot.
    • -
    • - snapshots
      +
    • snapshots
      Show the total number of a image sequence.
    @@ -398,15 +628,50 @@ IPCAM_guessFileFormat($) {
    - Attributes + Attributes
      +
    • + basicauth
      + If your camera supports authentication like http://username:password@domain.com/, you + can store your creditials within the basicauth attribute.
      + If you prefer to store the credentials in a file (take a look at the attribute credentials) + you have to set the placeholder {USERNAME} and {PASSWORD} in the basicauth string. + These placeholders will be replaced with the values from the credentials file.
      + Example:
      attr ipcam3 basicauth {USERNAME}:{PASSWORD} +
    • +
    • + cmd01, cmd02, cmd03, .. cmd13, cdm14, cdm15
      + It is possible to define up to 15 custom commands.
      + Examples:
      + attr ipcam cmd01 led_mode=0
      + attr ipcam cmd02 resolution=8
      +
    • +
    • + cmdPanLeft, cmdPanRight, cmdTiltUp, cmdTiltDown, cmdStep
      + Depending of the camera model, are different commands necessary.
      + Examples:
      + attr ipcam cmdTiltUp command=0
      + attr ipcam cmdTiltDown command=2
      + attr ipcam cmdPanLeft command=4
      + attr ipcam cmdPanRight command=6
      + attr ipcam cmdStep onstep
      +
    • +
    • + cmdPos01, cmdPos02, cmdPos03, .. cmdPos13, cmdPos14, cmdPos15, cmdPosHome + It is possible to define up to 15 predefined position in PTZ-mode.
      + Examples:
      + attr ipcam cmdPosHome command=25
      + attr ipcam cmdPos01 command=31
      + attr ipcam cmdPos02 command=33
      +
    • credentials
      Defines the location of the credentials file.
      If you prefer to store your cam credentials in a file instead be a part of the URI (see attributes path and query), set the full path with filename on this attribute.
      - Example: attr ipcam3 credentials /etc/fhem/ipcam.conf

      + Example:
      + attr ipcam3 credentials /etc/fhem/ipcam.conf

      The credentials file has the following structure:
      @@ -441,12 +706,27 @@ IPCAM_guessFileFormat($) {
             Defines the path and query component of the complete URI to get a snapshot of the
             camera. Is the full URI of your ip-cam for example http://CAMERA_IP/snapshot.cgi?user=admin&pwd=password,
             then only the path and query part is specified here (without the leading slash (/).
      - Example: attr ipcam3 path snapshot.cgi?user=admin&pwd=password

      + Example:
      + attr ipcam3 path snapshot.cgi?user=admin&pwd=password

      If you prefer to store the credentials in a file (take a look at the attribute credentials) - you have to set the placeholder USERNAME and PASSWORD in the path string. These placeholders + you have to set the placeholder {USERNAME} and {PASSWORD} in the path string. These placeholders will be replaced with the values from the credentials file.
      - Example: attr ipcam3 path snapshot.cgi?user=USERNAME&pwd=PASSWORD + Example:
      + attr ipcam3 path snapshot.cgi?user={USERNAME}&pwd={PASSWORD} +
    • +
    • + pathCmd
      + Defines a path for the custom commands, if it is necessary.
      + Example:
      + attr ipcam3 pathCmd set_misc.cgi +
    • +
    • + pathPanTilt
      + Defines a path for the PTZ-mode commands pan, tilt and pos, + if it is necessary.
      + Example:
      + attr ipcam3 pathPanTilt decoder_control.cgi?user={USERNAME}&pwd={PASSWORD}
    • showtime
    • @@ -461,12 +741,15 @@ IPCAM_guessFileFormat($) { If you like a timestamp instead a sequentially number, take a look at the attribute timestamp.
      All files are overwritten on every get <name> image command (except: snapshots with a timestamp. So, keep an eye on your diskspace if you use a timestamp extension!).
      - Example: attr ipcam3 snapshots 5 + Example:
      + attr ipcam3 snapshots 5
    • storage
      - Defines the location for the file storage of the snapshots. Default: $modpath/www/snapshots
      - Example: attr ipcam3 storage /srv/share/surveillance/snapshots + Defines the location for the file storage of the snapshots.
      + Default: $modpath/www/snapshots
      + Example:
      + attr ipcam3 storage /srv/share/surveillance/snapshots
    • timestamp
      @@ -479,7 +762,7 @@ IPCAM_guessFileFormat($) {
      - Generated events + Generated events
      • last: <name_of_device>_snapshot.<image_extension>
      • snapshots: <total_number_of_taken_snapshots_at_end>
      • @@ -488,6 +771,6 @@ IPCAM_guessFileFormat($) {
      - =end html =cut + diff --git a/fhem/FHEM/HttpUtils.pm b/fhem/FHEM/HttpUtils.pm index 523499d8d..77c708e0a 100644 --- a/fhem/FHEM/HttpUtils.pm +++ b/fhem/FHEM/HttpUtils.pm @@ -5,6 +5,7 @@ package main; use strict; use warnings; use IO::Socket::INET; +use MIME::Base64; my %ext2MIMEType= qw{ txt text/plain @@ -62,13 +63,13 @@ CustomGetFileFromURL($$@) $loglevel = 1 if(!$loglevel); my $displayurl= $quiet ? "" : $url; - if($url !~ /^(http|https):\/\/([^:\/]+)(:\d+)?(\/.*)$/) { + if($url !~ /^(http|https):\/\/(([^:\/]+):([^:\/]+)@)?([^:\/]+)(:\d+)?(\/.*)$/) { Log $loglevel, "CustomGetFileFromURL $displayurl: malformed or unsupported URL"; return undef; } - my ($protocol,$host,$port,$path)= ($1,$2,$3,$4); - + my ($protocol,$authstring,$user,$pwd,$host,$port,$path)= ($1,$2,$3,$4,$5,$6,$7); + if(defined($port)) { $port =~ s/^://; } else { @@ -76,6 +77,10 @@ CustomGetFileFromURL($$@) } $path= '/' unless defined($path); + my $auth64; + if(defined($authstring)) { + $auth64 = encode_base64("$user:$pwd",""); + } my $conn; if($protocol eq "https") { @@ -96,6 +101,9 @@ CustomGetFileFromURL($$@) $host =~ s/:.*//; my $hdr = ($data ? "POST" : "GET")." $path HTTP/1.0\r\nHost: $host\r\n"; + if(defined($authstring)) { + $hdr .= "Authorization: Basic $auth64\r\n"; + } if(defined($data)) { $hdr .= "Content-Length: ".length($data)."\r\n"; $hdr .= "Content-Type: application/x-www-form-urlencoded";