diff --git a/CHANGED b/CHANGED index 381541270..0e1d532d7 100644 --- a/CHANGED +++ b/CHANGED @@ -57,6 +57,7 @@ - feature: FhemUtils/release.pm for the new update process added. (M. Fischer) - bugfix: correct one-time relative at commands after reboot - feature: ZWave added + - feature: module IPCAM added. (M. Fischer) - 2011-12-31 (5.2) - bugfix: applying smallscreen attributes to firefox/opera diff --git a/FHEM/95_IPCAM.pm b/FHEM/95_IPCAM.pm new file mode 100644 index 000000000..3513f99ec --- /dev/null +++ b/FHEM/95_IPCAM.pm @@ -0,0 +1,285 @@ +################################################################ +# $Id: $ +# +# (c) 2012 Copyright: Martin Fischer (m_fischer at gmx dot de) +# All rights reserved +# +# This script free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +################################################################ + +package main; +use strict; +use warnings; + +sub IPCAM_getSnapshot($); +sub IPCAM_guessFileFormat($); + +my %gets = ( + "image" => "", + "last" => "", + "snapshots" => "", +); + +##################################### +sub +IPCAM_Initialize($$) +{ + my ($hash) = @_; + + $hash->{DefFn} = "IPCAM_Define"; + $hash->{UndefFn} = "IPCAM_Undef"; + $hash->{GetFn} = "IPCAM_Get"; + $hash->{AttrList} = "delay credentials path query snapshots storage ". + "do_not_notify:1,0 showtime:1,0 ". + "loglevel:0,1,2,3,4,5,6 disable:0,1"; +} + +##################################### +sub +IPCAM_Define($$) { + my ($hash, $def) = @_; + + # define IPCAM + # define webcam IPCAM 192.168.1.58:81 + + my @a = split("[ \t][ \t]*", $def); + + return "Wrong syntax: use 'define IPCAM '" + if(@a != 3); + + my $name = $a[0]; + my $auth = $a[2]; + + $hash->{AUTHORITY} = $auth; + $hash->{STATE} = "Defined"; + $hash->{SEQ} = 0; + + return undef; +} + +##################################### +sub +IPCAM_Undef($$) { + my ($hash, $name) = @_; + + delete($modules{IPCAM}{defptr}{$hash->{NAME}}); + RemoveInternalTimer($hash); + + return undef; +} + +##################################### +sub +IPCAM_Get($@) { + my ($hash, @a) = @_; + my $name = $hash->{NAME}; + my $seqImages; + my $seqDelay; + my $seqWait; + my $storage = (defined($attr{$name}{storage}) ? $attr{$name}{storage} : ""); + + # check syntax + return "argument is missing @a" + if(int(@a) != 2); + # check argument + return "Unknown argument $a[1], choose on of ".join(" ", sort keys %gets) + if(!defined($gets{$a[1]})); + # check attributes + return "Attribute 'path' is missing. Please add this attribute first!" + if(!defined($attr{$name}) || (defined($attr{$name}) && !defined($attr{$name}{path}))); + return "Attribute 'path' is defined but empty." + if(defined($attr{$name}{path}) && $attr{$name}{path} eq ""); + return "Attribute 'query' is defined but empty." + if(defined($attr{$name}{query}) && $attr{$name}{query} eq ""); + + my $arg = $a[1]; + + if($arg eq "image") { + + return "Attribute 'storage' is missing. Please add this attribute first!" + if(!$storage); + + $seqImages = int(defined($attr{$name}{snapshots}) ? $attr{$name}{snapshots} : 1); + $seqDelay = int(defined($attr{$name}{delay}) ? $attr{$name}{delay} : 0); + $seqWait = 0; + + # housekeeping after number of sequence has changed + my $readings = $hash->{READINGS}; + foreach my $r (sort keys %{$readings}) { + if($r =~ /snapshot\d+/) { + my $n = $r; + $n =~ s/snapshot//; + delete $readings->{$r} if( $r =~ m/snapshot/ && int($n) > $seqImages); + Log 5, "IPCAM $name remove old reading: $r"; + + } + } + $hash->{READINGS}{snapshots}{VAL} = 0; + for (my $i=0;$i<$seqImages;$i++) { + InternalTimer(gettimeofday()+$seqWait, "IPCAM_getSnapshot", $hash, 0); + $seqWait = $seqWait + $seqDelay; + } + return undef; + + } elsif(defined($hash->{READINGS}{$arg})) { + + if(defined($hash->{READINGS}{$arg}{VAL})) { + return "$name $arg => $hash->{READINGS}{$arg}{VAL}"; + } else { + return "$name $arg => undef"; + } + + } + +} + +##################################### +sub +IPCAM_getSnapshot($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + my $camAuth = $hash->{AUTHORITY}; + my $camURI; + my $camPath; + my $camQuery; + my $camCredentials; + my $imageFile; + my $imageFormat; + my $lastSnapshot; + my $snapshot; + my $dateTime; + my $seqImages = int(defined($attr{$name}{snapshots}) ? $attr{$name}{snapshots} : 1); + my $seq = int(defined($hash->{SEQ}) ? $hash->{SEQ} : 0); + my $storage = (defined($attr{$name}{storage}) ? $attr{$name}{storage} : ""); + + if(!$storage) { + RemoveInternalTimer($hash); + return "Attribute 'storage' is missing. Please add this attribute first!"; + } + + $camPath = $attr{$name}{path}; + $camQuery = $attr{$name}{query} + if(defined($attr{$name}{query}) && $attr{$name}{query} ne ""); + + $camURI = "http://$camAuth/$camPath"; + $camURI .= "?$camQuery" if($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}"; + RemoveInternalTimer($hash); + return undef; + } + my @cfg = ; + close(CFG); + my %credentials; + eval join("", @cfg); + $camURI =~ s/{USERNAME}/$credentials{$name}{username}/; + $camURI =~ s/{PASSWORD}/$credentials{$name}{password}/; + } + } + + $dateTime = TimeNow(); + + $snapshot = GetFileFromURL($camURI); + + $imageFormat = IPCAM_guessFileFormat(\$snapshot); + + my @imageTypes = qw(JPEG PNG GIF TIFF BMP ICO PPM XPM XBM SVG); + + if( ! grep { $_ eq "$imageFormat"} @imageTypes) { + Log 1, "IPCAM $name Wrong or not supported image format: $imageFormat"; + RemoveInternalTimer($hash); + return undef; + } + + Log GetLogLevel($name,5), "IPCAM $name Image Format: $imageFormat"; + + readingsBeginUpdate($hash); + if($seq < $seqImages) { + $seq++; + $lastSnapshot = $name."_snapshot.".lc($imageFormat); + $imageFile = $name."_snapshot".$seq.".".lc($imageFormat); + if(!open(FH, ">$storage/$lastSnapshot")) { + Log 1, "IPCAM $name Can't write $storage/$lastSnapshot: $!"; + RemoveInternalTimer($hash); + readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1)); + return undef; + } + print FH $snapshot; + close(FH); + Log 5, "IPCAM $name snapshot $storage/$lastSnapshot written."; + if(!open(FH, ">$storage/$imageFile")) { + Log 1, "IPCAM $name Can't write $storage/$imageFile: $!"; + RemoveInternalTimer($hash); + readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1)); + return undef; + } + print FH $snapshot; + close(FH); + Log 5, "IPCAM $name snapshot $storage/$imageFile written."; + readingsUpdate($hash,"last",$lastSnapshot); + $hash->{STATE} = "last: $dateTime"; + $hash->{READINGS}{"snapshot$seq"}{TIME} = $dateTime; + $hash->{READINGS}{"snapshot$seq"}{VAL} = $imageFile; + } + + Log GetLogLevel($name,4), "IPCAM $name image: $imageFile"; + + if($seq == $seqImages) { + readingsUpdate($hash,"snapshots",$seq); + $seq = 0; + } + readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1)); + $hash->{SEQ} = $seq; + + return undef; +} + +##################################### +sub +IPCAM_guessFileFormat($) { + my ($src) = shift; + my $header; + my $srcHeader; + + open(my $s, "<", $src) || return "can't open source image: $!"; + $src = $s; + + my $reading = read($src, $srcHeader, 64); + return "error while reading source image: $!" if(!$reading); + + local($_) = $srcHeader; + return "JPEG" if /^\xFF\xD8/; + return "PNG" if /^\x89PNG\x0d\x0a\x1a\x0a/; + return "GIF" if /^GIF8[79]a/; + return "TIFF" if /^MM\x00\x2a/; + return "TIFF" if /^II\x2a\x00/; + return "BMP" if /^BM/; + return "ICO" if /^\000\000\001\000/; + return "PPM" if /^P[1-6]/; + return "XPM" if /(^\/\* XPM \*\/)|(static\s+char\s+\*\w+\[\]\s*=\s*{\s*"\d+)/; + return "XBM" if /^(?:\/\*.*\*\/\n)?#define\s/; + return "SVG" if /^(<\?xml|[\012\015\t ]*FS20   HMS   HMLAN   + IPCAM   IPWE   IT   ITACH_RELAY   @@ -7881,6 +7882,172 @@ Terminating
+ + +

IPCAM

+
    +
    + + + Define +
      + define <name> IPCAM <ip[:port]> +

      + + 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.
      + 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.
      + 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:
      +
        + 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 motiondetection 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:
      +
        + define MOTION.not.02 notify ipcam:.*snapshots.* { myEmailFuntion("ipcam") }
        +

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

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

      + +
    + + Set
      N/A

    + + + Get +
      + get <name> <value> +

      + where value is one of:
      +
        +
      • + 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
        + Show the name of the last snapshot. +
      • +
      • + snapshots
        + Show the total number of a image sequence. +
      • +
      +
    +
    + + + Attributes +
      +
    • + 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

      + + The credentials file has the following structure:
      +
      +      #
      +      # Webcam credentials
      +      # 
      +      $credentials{<name_cam1>}{username} = "<your_username>";
      +      $credentials{<name_cam1>}{password} = "<your_password>";
      +      $credentials{<name_cam2>}{username} = "<your_username>";
      +      $credentials{<name_cam2>}{password} = "<your_password>";
      +      ...
      +      
      + Replace <name_cam1> respectively <name_cam2> + with the names of your defined ip-cams and <your_username> respectively + <your_password> with your credentials (all without the brackets + < and >!). +
    • +
    • + delay
      + Defines the time interval between snapshots in seconds.
      + If more then one snapshot is taken, then it makes sense to define a short delay + between the snapshots. On the one hand, the camera is not addressed in short intervals + and the second may be better represented movements between images.
      + Example: attr ipcam3 delay 10 +
    • +
    • disable
    • +
    • do_not_notify
    • +
    • loglevel
    • +
    • + path
      + 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

      + + 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 + will be replaced with the values from the credentials file.
      + Example: attr ipcam3 path snapshot.cgi?user=USERNAME&pwd=PASSWORD +
    • +
    • showtime
    • +
    • + snapshots
      + Defines the total number of snapshots to be taken with the get <name> image command. + If this attribute is not defined, then the default value is 1.
      + The snapshots are stored in the given path of the attribute storage and are + numbered sequentially (starts with 1) like snapshot1, snapshot2, etc. + Furthermore, an additional file last will be saved, which is identical with + the last snapshot-image. The module checks the imagetype and stores all these files with + the correct extension, e.g. snapshot1.jpeg.
      + All files are overwritten on every get <name> image command.
      + Example: attr ipcam3 snapshots 5 +
    • +
    • + storage
      + Defines the location for the file storage of the snapshots.
      + Example: attr ipcam3 storage /srv/share/surveillance/snapshots +
    • +
    +
    + + + Generated events +
      +
    • last: <name_of_device>_snapshot.<image_extension>
    • +
    • snapshots: <total_number_of_taken_snapshots_at_end>
    • +
    +
    + +
+ +

IPWE

    @@ -8641,7 +8808,7 @@ KlikAanKlikUit, NEXA, CHACON, HomeEasy UK.
    You need to define an RFXtrx433 Example:
      define lamp ZWave 00ce2074 9
      - attr lamp classes SWITCH_BINARY BASIC MANUFACTURER_SPECIFIC VERSION SWITCH_ALL ASSOCIATION METER CONFIGURATION ALARM
      + attr lamp classes SWITCH_BINARY BASIC MANUFACTURER_SPECIFIC VERSION SWITCH_ALL ASSOCIATION METER CONFIGURATION ALARM

@@ -11199,4 +11366,7 @@ The .gnuplot file consists of 3 parts: +