From 624385760a46fea2a74de4aaffbbe04d6b0209dc Mon Sep 17 00:00:00 2001 From: rudolfkoenig Date: Fri, 1 Mar 2013 11:09:18 +0000 Subject: [PATCH] event-min-interval FBAHA/FBDECT git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@2835 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- CHANGED | 2 + FHEM/00_FBAHA.pm | 446 ++++++++++++++++++++++++++++++++++ FHEM/10_FBDECT.pm | 354 +++++++++++++++++++++++++++ FHEM/98_autocreate.pm | 15 +- docs/commandref_frame.html | 9 + docs/commandref_frame_DE.html | 20 +- fhem.pl | 46 +++- 7 files changed, 868 insertions(+), 24 deletions(-) create mode 100755 FHEM/00_FBAHA.pm create mode 100755 FHEM/10_FBDECT.pm diff --git a/CHANGED b/CHANGED index 03ea28cc4..cbdeac94a 100644 --- a/CHANGED +++ b/CHANGED @@ -92,6 +92,8 @@ added, Extend devStateIcon, js setting of attr values in detail screen, live slider update in detail and room view - feature: added support for third-party packages to 98_update.pm (M. Fischer) + - feature: FBAHA/FBDECT for FRITZ!DECT devices + - feature: event-min-interval Attribute - 2012-10-28 (5.3) - feature: added functions trim, ltrim, rtrim, UntoggleDirect, diff --git a/FHEM/00_FBAHA.pm b/FHEM/00_FBAHA.pm new file mode 100755 index 000000000..864bcb425 --- /dev/null +++ b/FHEM/00_FBAHA.pm @@ -0,0 +1,446 @@ +############################################## +# $Id: 00_FBAHA.pm 2777 2013-02-20 08:02:01Z rudolfkoenig $ +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +sub FBAHA_Read($@); +sub FBAHA_Write($$$); +sub FBAHA_ReadAnswer($$$); +sub FBAHA_Ready($); + +sub FBAHA_getDevList($$); + + +sub +FBAHA_Initialize($) +{ + my ($hash) = @_; + + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + +# Provider + $hash->{ReadFn} = "FBAHA_Read"; + $hash->{WriteFn} = "FBAHA_Write"; + $hash->{ReadyFn} = "FBAHA_Ready"; + $hash->{UndefFn} = "FBAHA_Undef"; + $hash->{ShutdownFn} = "FBAHA_Undef"; + $hash->{ReadAnswerFn} = "FBAHA_ReadAnswer"; + +# Normal devices + $hash->{DefFn} = "FBAHA_Define"; + $hash->{GetFn} = "FBAHA_Get"; + $hash->{SetFn} = "FBAHA_Set"; + $hash->{AttrList}= "dummy:1,0 loglevel:0,1,2,3,4,5,6 "; +} + +my %gets = ("devList"=>1); +my %sets = ("createDevs"=>1); + +##################################### +sub +FBAHA_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + if(@a != 3) { + return "wrong syntax: define FBAHA hostname:2002"; + } + + DevIo_CloseDev($hash); + + my $name = $a[0]; + my $dev = $a[2]; + + $hash->{Clients} = ":FBDECT:"; + my %matchList = ( "1:FBDECT" => ".*" ); + $hash->{MatchList} = \%matchList; + + $hash->{DeviceName} = $dev; + my $ret = DevIo_OpenDev($hash, 0, "FBAHA_DoInit"); + return $ret; +} + + +##################################### +sub +FBAHA_Set($@) +{ + my ($hash, @a) = @_; + my $name = shift @a; + + return "set $name needs at least one parameter" if(@a < 1); + my $type = shift @a; + + return "Unknown argument $type, choose one of " . join(" ", sort keys %sets) + if(!defined($sets{$type})); + + if($type eq "createDevs") { + my @arg = FBAHA_getDevList($hash,0); + foreach my $arg (@arg) { + if($arg =~ m/ID:(\d+).*PROP:(.*)/) { + my ($i,$p) = ($1,$2,$3); + Log 1, "UNDEFINED FBDECT_$i FBDECT $i $p"; + } + } + } + + return undef; +} + +##################################### +sub +FBAHA_Get($@) +{ + my ($hash, @a) = @_; + my $name = shift @a; + + return "get $name needs at least one parameter" if(@a < 1); + my $type = shift @a; + + return "Unknown argument $type, choose one of ". join(" ", sort keys %gets) + if(!defined($gets{$type})); + + if($type eq "devList") { + return join("\n", FBAHA_getDevList($hash,0)); + } + + return undef; +} + +sub +FBAHA_getDevList($$) +{ + my ($hash, $onlyId) = @_; + + FBAHA_Write($hash, "05", "00000000"); # CONFIG_REQ + my ($err, $data) = FBAHA_ReadAnswer($hash, "CONFIG_RSP", "^06"); + return ($err) if($err); + + $data = substr($data, 32); # Header + return FBAHA_configInd($data, $onlyId); +} + +sub +FBAHA_configInd($$) +{ + my ($data, $onlyId) = @_; + + + my @answer; + while(length($data) >= 288) { + my $id = hex(substr($data, 0, 4)); + my $act = hex(substr($data, 4, 2)); + my $typ = hex(substr($data, 8, 4)); + my $lsn = hex(substr($data, 16, 8)); + my $nam = pack("H*",substr($data,24,160)); $nam =~ s/\x0//g; + + $act = ($act == 2 ? "active" : ($act == 1 ? "inactive" : "removed")); + + my %tl = ( 2=>"AVM FRITZ!Dect Powerline 546E", 9=>"AVM FRITZ!Dect 200"); + $typ = $tl{$typ} ? $tl{$typ} : "unknown($typ)"; + + my %ll = (7=>"powerMeter",9=>"switch"); + $lsn = join ",", map { $ll{$_} if((1 << $_) & $lsn) } sort keys %ll; + + my $dlen = hex(substr($data, 280, 8))*2; # DATA MSG + + push @answer, "NAME:$nam, ID:$id, $act, TYPE:$typ PROP:$lsn" + if(!$onlyId || $onlyId == $id); + + if($onlyId && $onlyId == $id) { + my $mnf = hex(substr($data,184, 4)); # empty/0 + my $idf = substr($data,192,40); # empty/0 + my $frm = substr($data,232,40); # empty/0 + push @answer, " MANUF:$mnf"; + push @answer, " UniqueID:$idf"; + push @answer, " Firmware:$frm"; + push @answer, substr($data, 288, $dlen); + return @answer; + } + $data = substr($data, 288+$dlen); # rest + } + return @answer; +} + +##################################### +sub +FBAHA_DoInit($) +{ + my $hash = shift; + my $name = $hash->{NAME}; + Log 1, "FBAHA_DoInit called"; + + FBAHA_Write($hash, "00", "00010001"); + my ($err, $data) = FBAHA_ReadAnswer($hash, "REGISTER", "^01"); + if($err) { + Log 1, $err; + $hash->{STATE} = "???"; + return 1; + } + + if($data =~ m/^01030010(........)/) { + $hash->{STATE} = "Initialized"; + $hash->{HANDLE} = $1; + + } else { + Log 1, "Got bogus answer for REGISTER request: $data"; + $hash->{STATE} = "???"; + + } + + FBAHA_Write($hash, "03", "0000028200000000"); # LISTEN + return undef; +} + +##################################### +sub +FBAHA_Undef($@) +{ + my ($hash, $arg) = @_; + FBAHA_Write($hash, "02", ""); # RELEASE + DevIo_CloseDev($hash); + return undef; +} + +##################################### +sub +FBAHA_Write($$$) +{ + my ($hash,$fn,$msg) = @_; + + $msg = sprintf("%s03%04x%s%s", $fn, length($msg)/2+8, + $hash->{HANDLE} ? $hash->{HANDLE} : "00000000", $msg); + DevIo_SimpleWrite($hash, $msg, 1); +} + +##################################### +# called from the global loop, when the select for hash->{FD} reports data +sub +FBAHA_Read($@) +{ + my ($hash, $local, $regexp) = @_; + + my $buf = ($local ? $local : DevIo_SimpleRead($hash)); + return "" if(!defined($buf)); + + my $name = $hash->{NAME}; + my $ll5 = GetLogLevel($name,5); + + $buf = unpack('H*', $buf); + my $data = ($hash->{PARTIAL} ? $hash->{PARTIAL} : ""); + + # drop old data + if($data) { + $data = "" if(gettimeofday() - $hash->{READ_TS} > 1); + delete($hash->{READ_TS}); + } + + Log $ll5, "FBAHA/RAW: $data/$buf"; + $data .= $buf; + + my $msg; + while(length($data) >= 16) { + my $len = hex(substr($data, 4,4))*2; + last if($len > length($data)); + $msg = substr($data, 0, $len); + $data = substr($data, $len); + last if(defined($local) && (!defined($regexp) || ($msg =~ m/$regexp/))); + + $hash->{"${name}_MSGCNT"}++; + $hash->{"${name}_TIME"} = TimeNow(); + $hash->{RAWMSG} = $msg; + my %addvals = (RAWMSG => $msg); + Dispatch($hash, $msg, \%addvals) if($init_done); + $msg = undef; + } + + $hash->{PARTIAL} = $data; + $hash->{READ_TS} = gettimeofday() if($data); + return $msg if(defined($local)); + return undef; +} + +##################################### +# This is a direct read for commands like get +sub +FBAHA_ReadAnswer($$$) +{ + my ($hash, $arg, $regexp) = @_; + return ("No FD (dummy device?)", undef) + if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD}))); + my $to = ($hash->{RA_Timeout} ? $hash->{RA_Timeout} : 3); + + for(;;) { + return ("Device lost when reading answer for get $arg", undef) + if(!$hash->{FD}); + my $rin = ''; + vec($rin, $hash->{FD}, 1) = 1; + my $nfound = select($rin, undef, undef, $to); + if($nfound < 0) { + next if ($! == EAGAIN() || $! == EINTR() || $! == 0); + my $err = $!; + DevIo_Disconnected($hash); + return("FBAHA_ReadAnswer $arg: $err", undef); + } + return ("Timeout reading answer for get $arg", undef) + if($nfound == 0); + my $buf = DevIo_SimpleRead($hash); + return ("No data", undef) if(!defined($buf)); + + my $ret = FBAHA_Read($hash, $buf, $regexp); + return (undef, $ret) if(defined($ret)); + } +} + +##################################### +sub +FBAHA_Ready($) +{ + my ($hash) = @_; + + return DevIo_OpenDev($hash, 1, "FBAHA_DoInit") + if($hash->{STATE} eq "disconnected"); + return 0; +} + +1; + +=pod +=begin html + + +

FBAHA

+ + + +=end html + +=begin html_DE + + +

FBAHA

+ +=end html_DE + +=cut diff --git a/FHEM/10_FBDECT.pm b/FHEM/10_FBDECT.pm new file mode 100755 index 000000000..4203c03a6 --- /dev/null +++ b/FHEM/10_FBDECT.pm @@ -0,0 +1,354 @@ +############################################## +# $Id: 10_FBDECT.pm 2779 2013-02-21 08:52:27Z rudolfkoenig $ +package main; + +# TODO: test multi-dev, test on the FB + +use strict; +use warnings; +use SetExtensions; + +sub FBDECT_Parse($$@); +sub FBDECT_Set($@); +sub FBDECT_Get($@); +sub FBDECT_Cmd($$@); + +my @fbdect_models = qw( + "AVM FRITZ!Dect Powerline 546E" + "AVM FRITZ!Dect 200" +); + +my %fbdect_payload = ( + 7 => { n=>"connected" }, + 8 => { n=>"disconnected" }, + 10 => { n=>"configChanged" }, + 15 => { n=>"state", fmt=>'hex($pyld)?"on":"off"' }, + 18 => { n=>"current", fmt=>'sprintf("%0.4f A", hex($pyld)/10000)' }, + 19 => { n=>"voltage", fmt=>'sprintf("%0.3f V", hex($pyld)/1000)' }, + 20 => { n=>"power", fmt=>'sprintf("%0.2f W", hex($pyld)/100)' }, + 21 => { n=>"energy", fmt=>'sprintf("%0.0f Wh",hex($pyld))' }, + 22 => { n=>"powerFactor", fmt=>'sprintf("%0.3f", hex($pyld))' }, + 23 => { n=>"temperature", fmt=>'sprintf("%0.1f C", hex($pyld)/10)' }, +); + + +sub +FBDECT_Initialize($) +{ + my ($hash) = @_; + $hash->{Match} = ".*"; + $hash->{SetFn} = "FBDECT_Set"; + $hash->{GetFn} = "FBDECT_Get"; + $hash->{DefFn} = "FBDECT_Define"; + $hash->{UndefFn} = "FBDECT_Undef"; + $hash->{ParseFn} = "FBDECT_Parse"; + $hash->{AttrList} = + "IODev do_not_notify:1,0 ignore:1,0 dummy:1,0 showtime:1,0 ". + "loglevel:0,1,2,3,4,5,6 $readingFnAttributes " . + "model:".join(",", sort @fbdect_models); +} + + +############################# +sub +FBDECT_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + my $name = shift @a; + my $type = shift(@a); # always FBDECT + + my $u = "wrong syntax for $name: define FBDECT id props"; + return $u if(int(@a) != 2); + + my $id = shift @a; + return "define $name: wrong id ($id): need a number" + if( $id !~ m/^\d+$/i ); + $hash->{id} = $id; + $hash->{props} = shift @a; + + $modules{FBDECT}{defptr}{$id} = $hash; + AssignIoPort($hash); + return undef; +} + +################################### +my %sets = ("on"=>1, "off"=>1); +sub +FBDECT_Set($@) +{ + my ($hash, @a) = @_; + my $ret = undef; + my $cmd = $a[1]; + + if(!$sets{$cmd}) { + return SetExtensions($hash, join(" ", sort keys %sets), @a); + } + my $relay = sprintf("%08x%04x0000%08x", 15, 4, $cmd eq "on" ? 1 : 0); + my $msg = sprintf("%04x0000%08x$relay", $hash->{id}, length($relay)/2); + IOWrite($hash, "07", $msg); + readingsSingleUpdate($hash, "state", "set_$cmd", 1); + return undef; +} + +my %gets = ("devInfo"=>1); +sub +FBDECT_Get($@) +{ + my ($hash, @a) = @_; + my $ret = undef; + my $cmd = ($a[1] ? $a[1] : ""); + + if(!$gets{$cmd}) { + return "Unknown argument $cmd, choose one of ".join(" ", sort keys %gets); + } + + if($cmd eq "devInfo") { + my @answ = FBAHA_getDevList($hash->{IODev}, $hash->{id}); + return $answ[0] if(@answ == 1); + my $d = pop @answ; + while($d) { + my ($ptyp, $plen, $pyld) = FBDECT_decodePayload($d); + push @answ, " $ptyp: $pyld"; + $d = substr($d, 16+$plen*2); + } + return join("\n", @answ); + } + return undef; +} + +################################### +sub +FBDECT_Parse($$@) +{ + my ($iodev, $msg, $local) = @_; + my $ioName = $iodev->{NAME}; + my $ll4 = AttrVal($ioName, "loglevel", 4); + + my $mt = substr($msg, 0, 2); + if($mt ne "07" && $mt ne "04") { + Log 1, "FBDECT: unknown message type $mt"; + return; + } + + my $id = hex(substr($msg, 16, 4)); + my $hash = $modules{FBDECT}{defptr}{$id}; + if(!$hash) { + my $ret = "UNDEFINED FBDECT_$id FBDECT $id switch"; + Log 1, $ret; + DoTrigger("global", $ret); + return ""; + } + + readingsBeginUpdate($hash); + + if($mt eq "07") { + my $d = substr($msg, 32); + while($d) { + my ($ptyp, $plen, $pyld) = FBDECT_decodePayload($d); + readingsBulkUpdate($hash, $ptyp, $pyld); + $d = substr($d, 16+$plen*2); + } + } + if($mt eq "04") { + my @answ = FBAHA_configInd(substr($msg,16), $id); + my $d = pop @answ; + while($d) { + my ($ptyp, $plen, $pyld) = FBDECT_decodePayload($d); + last if(!$plen); + push @answ, " $ptyp: $pyld"; + $d = substr($d, 16+$plen*2); + } + # Ignore the rest, is too confusing. + @answ = grep /state:/, @answ; + my ($undef, $state) = split(": ", $answ[0], 2); + readingsBulkUpdate($hash, "state", $state) if($state); + Log 1, join("\n", @answ); + } + + readingsEndUpdate($hash, 1); + + return $hash->{NAME}; +} + +sub +FBDECT_decodePayload($) +{ + my ($d) = @_; + my $ptyp = hex(substr($d, 0, 8)); + my $plen = hex(substr($d, 8, 4)); + my $pyld = substr($d, 16, $plen*2); + if($fbdect_payload{$ptyp}) { + $pyld = eval $fbdect_payload{$ptyp}{fmt} if($fbdect_payload{$ptyp}{fmt}); + $ptyp = $fbdect_payload{$ptyp}{n}; + } + return ($ptyp, $plen, $pyld); +} + +##################################### +sub +FBDECT_Undef($$) +{ + my ($hash, $arg) = @_; + my $homeId = $hash->{homeId}; + my $id = $hash->{id}; + delete $modules{FBDECT}{defptr}{$id}; + return undef; +} + +1; + +=pod +=begin html + + +

FBDECT

+
    + This module is used to control AVM FRITZ!DECT devices via FHEM, see also the + FBAHA module for the base. +

    + + Define +
      + define <name> FBDECT <homeId> <id> [classes] +
      +
      + <id> is the id of the device, the classes argument ist ignored for now. +
      + Example: +
        + define lamp FBDECT 16 switch,powerMeter
        +
      + Note:Usually the device is created via + autocreate +
    +
    +
    + Set +
      +
    • on/off
      + set the device on or off.
    • +
    • + set extensions are supported.
    • +
    +
    + + + Get +
      +
    • devInfo
      + report device information
    • +
    +
    + + + Attributes + +
    + + + Generated events: +
      +
    • on
    • +
    • off
    • +
    • set_on
    • +
    • set_off
    • +
    • current: $v A
    • +
    • voltage: $v V
    • +
    • power: $v W
    • +
    • energy: $v Wh
    • +
    • powerFactor: $v"
    • +
    • temperature: $v C
    • +
    +
+ +=end html + +=begin html_DE + + +

FBDECT

+
    + Dieses Modul wird verwendet, um AVM FRITZ!DECT Geräte via FHEM zu + steuern, siehe auch das FBAHA Modul für die + Anbindung an das FRITZ!Box. +

    + + Define +
      + define <name> FBDECT <homeId> <id> [classes] +
      +
      + <id> ist das Geräte-ID, das Argument wird z.Zt ignoriert. +
      + Beispiel: +
        + define lampe FBDECT 16 switch,powerMeter
        +
      + Achtung:FBDECT Einträge werden noralerweise per + autocreate angelegt. +
    +
    +
    + Set +
      +
    • on/off
      + Gerät einschalten bzw. ausschalten.
    • +
    • + Die set extensions werden unterstützt.
    • +
    +
    + + + Get +
      +
    • devInfo
      + meldet Geräte-Informationen.
    • +
    +
    + + + Attribute + +
    + + + Generierte events: +
      +
    • on
    • +
    • off
    • +
    • set_on
    • +
    • set_off
    • +
    • current: $v A
    • +
    • voltage: $v V
    • +
    • power: $v W
    • +
    • energy: $v Wh
    • +
    • powerFactor: $v"
    • +
    • temperature: $v C
    • +
    +
+=end html_DE + +=cut diff --git a/FHEM/98_autocreate.pm b/FHEM/98_autocreate.pm index aafecd8a3..bf1d6879d 100644 --- a/FHEM/98_autocreate.pm +++ b/FHEM/98_autocreate.pm @@ -76,6 +76,10 @@ my %flogpar = ( # Lacrosse TX "CUL_TX.*" => { GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME" }, + + "FBDECT.*" + => { GPLOT => "power4:Power,", FILTER => "%NAME:power.*", + ATTR => "event-min-interval:power:120" }, ); # Do not create FileLog for the following devices. @@ -180,11 +184,13 @@ autocreate_Notify($$) next if(!$fl); my $flname = "FileLog_$name"; delete($defs{$flname}); # If we are re-creating it with createlog. - my ($gplot, $filter) = ("", $name); + my ($gplot, $filter, $devattr) = ("", $name, ""); foreach my $k (keys %flogpar) { next if($name !~ m/^$k$/); $gplot = $flogpar{$k}{GPLOT}; $filter = replace_wildcards($hash, $flogpar{$k}{FILTER}); + $devattr = $flogpar{$k}{ATTR}; + last; } $cmd = "$flname FileLog $fl $filter"; Log $ll2, "autocreate: define $cmd"; @@ -195,7 +201,12 @@ autocreate_Notify($$) } $attr{$flname}{room} = $room if($room); $attr{$flname}{logtype} = "${gplot}text"; - + if($devattr) { + foreach my $attrNV (split(" ", $devattr)) { + my ($an, $av) = split(":", $attrNV, 2); + $attr{$name}{$an} = $av; + } + } #################### next if(!AttrVal($me, "weblink", 1) || !$gplot); diff --git a/docs/commandref_frame.html b/docs/commandref_frame.html index feb89688f..fbb1999fc 100644 --- a/docs/commandref_frame.html +++ b/docs/commandref_frame.html @@ -370,6 +370,15 @@ A line ending with \ will be concatenated with the next one, so long lines new value identical to the old value, no event is created.
+ +
  • event-min-interval
    + This attribute takes a comma-separated list of reading:minInterval pairs. + You may use regular expressions for reading. Events will only be + generated, if at least minInterval seconds elapsed since the last reading + of the matched type. +

  • + + The precedence of event-on-update-reading and event-on-change-reading is as follows:
      diff --git a/docs/commandref_frame_DE.html b/docs/commandref_frame_DE.html index c5b070058..e52c52a60 100644 --- a/docs/commandref_frame_DE.html +++ b/docs/commandref_frame_DE.html @@ -355,15 +355,6 @@ Zeilen erstreckende Befehle, indem man keine \ am Zeilenende eingeben muss.

      Auswertung passiert bei jeder Änderung eines Readings. - -
    1. event-on-update-reading
      - If not set, every update of any reading creates an event, which e.g. is - handled by notify or FileLog. - The attribute takes a comma-separated list of readings. You may use regular - expressions in that list. If set, only updates of the listed readings - create events. -
    2. -
    3. event-on-update-reading
      Wenn nicht gesetzt, erzeugt jede Veränderung eines "readings" ein @@ -374,7 +365,6 @@ Zeilen erstreckende Befehle, indem man keine \ am Zeilenende eingeben muss.

    4. event-on-change-reading
      - Dieses Attribut enthält eine durch Kommata getrennte Liste von "readings". Wenn gesetzt, erzeugen nur Veränderungen der gelisteten "readings" ein Ereignis. Wenn die aktualiserten Werte der gelisteten @@ -390,7 +380,15 @@ Zeilen erstreckende Befehle, indem man keine \ am Zeilenende eingeben muss.

    5. Wenn ein "reading" in event-on-update-reading aufgeführt ist, erzeugt eine Aktualisierung ein Ereignis unabhängig ob das "reading" auch in event-on-change-reading aufgelistet ist.
    6. -
      +
      + + +
    7. event-min-interval
      + Dieses Attribut enthält eine durch Kommata getrennte Liste von + "readings:minInterval" Paare. readings kann ein regexp sein. Ein Event wird + nur dann generiert, falls seit dem letzten Auftreten des gleichen Events + mindestens minInterval Sekunden vergangen sind. +

    8. userReadings
      diff --git a/fhem.pl b/fhem.pl index 54a351610..f39e6ef1b 100755 --- a/fhem.pl +++ b/fhem.pl @@ -205,12 +205,14 @@ $modules{Global}{AttrList} = "verbose:1,2,3,4,5 mseclog:1,0 version nofork:1,0 logdir holiday2we " . "autoload_undefined_devices:1,0 dupTimeout latitude longitude " . "backupcmd backupdir backupsymlink backup_before_update " . - "exclude_from_update motd updatebranch uniqueID sendStatistics:onUpdate,manually,never ". + "exclude_from_update motd updatebranch uniqueID ". + "sendStatistics:onUpdate,manually,never ". "showInternalValues:1,0 "; $modules{Global}{AttrFn} = "GlobalAttr"; use vars qw($readingFnAttributes); -$readingFnAttributes = "event-on-change-reading event-on-update-reading stateFormat"; +$readingFnAttributes = "event-on-change-reading event-on-update-reading ". + "event-min-interval stateFormat"; %cmds = ( @@ -2109,9 +2111,7 @@ SignalHandling() sub TimeNow() { - my @t = localtime; - return sprintf("%04d-%02d-%02d %02d:%02d:%02d", - $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]); + return FmtDateTime(time()); } ##################################### @@ -2992,9 +2992,16 @@ readingsBeginUpdate($) my $name = $hash->{NAME}; # get timestamp - my $now = TimeNow(); - $hash->{".updateTime"} = time(); # in seconds since the epoch - $hash->{".updateTimestamp"} = $now; + my $now = gettimeofday(); + my $fmtDateTime = FmtDateTime($now); + $hash->{".updateTime"} = $now; # in seconds since the epoch + $hash->{".updateTimestamp"} = $fmtDateTime; + + my $attrminint = AttrVal($name, "event-min-interval", undef); + if($attrminint) { + my @a = split(/,/,$attrminint); + $hash->{".attrminint"} = \@a; + } my $attreocr= AttrVal($name, "event-on-change-reading", undef); if($attreocr) { @@ -3009,7 +3016,7 @@ readingsBeginUpdate($) } $hash->{CHANGED}= (); - return $now; + return $fmtDateTime; } sub @@ -3095,6 +3102,7 @@ readingsEndUpdate($$) delete $hash->{".updateTime"}; delete $hash->{".attreour"}; delete $hash->{".attreocr"}; + delete $hash->{".attrminint"}; # propagate changes @@ -3131,12 +3139,14 @@ readingsBulkUpdate($$$@) if(!defined($changed)) { $changed = (substr($reading,0,1) ne "."); # Dont trigger dot-readings } + $changed = 0 if($hash->{".ignoreEvent"}); + # check for changes only if reading already exists if($changed && defined($readings)) { # these flags determine if any of the "event-on" attributes are set - my $attreocr= $hash->{".attreocr"}; - my $attreour= $hash->{".attreour"}; + my $attreocr = $hash->{".attreocr"}; + my $attreour = $hash->{".attreour"}; # these flags determine whether the reading is listed in any of # the attributes @@ -3151,6 +3161,20 @@ readingsBulkUpdate($$$@) || $eour || ($eocr && ($value ne $readings->{VAL})); #Log 1, "EOCR:$eocr EOUR:$eour CHANGED:$changed"; + + my @v = grep { my $l = $_; + $l =~ s/:.*//; + ($reading=~ m/^$l$/) ? $_ : undef} @{$hash->{".attrminint"}}; + if($changed && @v) { + my (undef, $minInt) = split(":", $v[0]); + my $now = $hash->{".updateTime"}; + my $le = $hash->{".lastTime$reading"}; + if($le && $now-$le < $minInt) { + $changed = 0; + } else { + $hash->{".lastTime$reading"} = $now; + } + } } setReadingsVal($hash, $reading, $value, $hash->{".updateTimestamp"});