mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-03 16:56:54 +00:00
98_freezemon.pm: New attributes to ignore Devices + bug fixes.
git-svn-id: https://svn.fhem.de/fhem/trunk@16196 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
17243db401
commit
6da01bcfcd
@ -22,6 +22,9 @@
|
||||
#
|
||||
##############################################################################
|
||||
# Changelog:
|
||||
# 0.0.12: problem with older perl versions (Forum #764462)
|
||||
# Small improvement in device detection
|
||||
# added ignoreDev and ignorMode attribute
|
||||
# 0.0.11: added date to "get freeze" popup
|
||||
# fixed readingsbulkupdate behaviour
|
||||
# fixed that freezemon is inactive after restart
|
||||
@ -59,7 +62,6 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
@ -69,34 +71,32 @@ use POSIX;
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
use B qw(svref_2object);
|
||||
|
||||
|
||||
|
||||
my $version = "0.0.10";
|
||||
my $version = "0.0.12";
|
||||
|
||||
###################################
|
||||
sub freezemon_Initialize($) {
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
# Module specific attributes
|
||||
my @freezemon_attr =
|
||||
( "fm_forceApptime:0,1 fm_freezeThreshold disable:0,1 fm_log");
|
||||
("fm_forceApptime:0,1 fm_freezeThreshold disable:0,1 fm_log fm_ignoreDev fm_ignoreMode:off,single,all");
|
||||
|
||||
$hash->{GetFn} = "freezemon_Get";
|
||||
$hash->{SetFn} = "freezemon_Set";
|
||||
$hash->{DefFn} = "freezemon_Define";
|
||||
$hash->{UndefFn} = "freezemon_Undefine";
|
||||
$hash->{NotifyFn} = "freezemon_Notify";
|
||||
$hash->{NotifyDev} = "global";
|
||||
$hash->{NotifyOrderPrefix} = "99-"; # we want to be notified late.
|
||||
$hash->{AttrFn} = "freezemon_Attr";
|
||||
$hash->{AttrList} = join( " ", @freezemon_attr ) . " " . $readingFnAttributes;
|
||||
$hash->{GetFn} = "freezemon_Get";
|
||||
$hash->{SetFn} = "freezemon_Set";
|
||||
$hash->{DefFn} = "freezemon_Define";
|
||||
$hash->{UndefFn} = "freezemon_Undefine";
|
||||
$hash->{NotifyFn} = "freezemon_Notify";
|
||||
$hash->{NotifyDev} = "global";
|
||||
$hash->{NotifyOrderPrefix} = "99-"; # we want to be notified late.
|
||||
$hash->{AttrFn} = "freezemon_Attr";
|
||||
$hash->{AttrList} = join( " ", @freezemon_attr ) . " " . $readingFnAttributes;
|
||||
|
||||
|
||||
#map new Attribute names
|
||||
$hash->{AttrRenameMap} = { "fmForceApptime" => "fm_forceApptime:",
|
||||
"fmFreezeTime" => "fm_freezeThreshold"
|
||||
};
|
||||
#map new Attribute names
|
||||
$hash->{AttrRenameMap} = {
|
||||
"fmForceApptime" => "fm_forceApptime:",
|
||||
"fmFreezeTime" => "fm_freezeThreshold"
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ sub freezemon_Define($$) {
|
||||
my ( $hash, $def ) = @_;
|
||||
my @a = split( "[ \t][ \t]*", $def );
|
||||
|
||||
RemoveInternalTimer($hash);
|
||||
RemoveInternalTimer($hash);
|
||||
|
||||
my $usage = "syntax: define <name> freezemon";
|
||||
|
||||
@ -118,18 +118,18 @@ sub freezemon_Define($$) {
|
||||
Log3 $name, 3, "freezemon defined $name $type";
|
||||
|
||||
$hash->{VERSION} = $version;
|
||||
$hash->{NAME} = $name;
|
||||
$hash->{NAME} = $name;
|
||||
|
||||
# start the timer
|
||||
Log3 $name, 5, "[$name] => Define IsDisabled:".IsDisabled($name)." init_done:$init_done";
|
||||
if (!IsDisabled($name) && $init_done) {
|
||||
freezemon_start($hash)
|
||||
}
|
||||
elsif (IsDisabled($name)) {
|
||||
$hash->{STATE} = "inactive";
|
||||
$hash->{helper}{DISABLED} = 1;
|
||||
}
|
||||
$hash->{VERSION} = $version;
|
||||
# start the timer
|
||||
Log3 $name, 5, "[$name] => Define IsDisabled:" . IsDisabled($name) . " init_done:$init_done";
|
||||
if ( !IsDisabled($name) && $init_done ) {
|
||||
freezemon_start($hash);
|
||||
}
|
||||
elsif ( IsDisabled($name) ) {
|
||||
$hash->{STATE} = "inactive";
|
||||
$hash->{helper}{DISABLED} = 1;
|
||||
}
|
||||
$hash->{VERSION} = $version;
|
||||
|
||||
return undef;
|
||||
}
|
||||
@ -144,164 +144,218 @@ sub freezemon_Undefine($$) {
|
||||
return undef;
|
||||
}
|
||||
###################################
|
||||
sub freezemon_Notify($$)
|
||||
{
|
||||
my ($hash, $dev) = @_;
|
||||
my $name = $hash->{NAME}; # own name / hash
|
||||
my $events = deviceEvents($dev,1);
|
||||
sub freezemon_Notify($$) {
|
||||
my ( $hash, $dev ) = @_;
|
||||
my $name = $hash->{NAME}; # own name / hash
|
||||
my $events = deviceEvents( $dev, 1 );
|
||||
|
||||
return "" if(IsDisabled($name)); # Return without any further action if the module is disabled
|
||||
return if(!grep(m/^INITIALIZED|REREADCFG$/, @{$events}));
|
||||
return ""
|
||||
if ( IsDisabled($name) ); # Return without any further action if the module is disabled
|
||||
return if ( !grep( m/^INITIALIZED|REREADCFG$/, @{$events} ) );
|
||||
|
||||
freezemon_start($hash);
|
||||
}
|
||||
###################################
|
||||
sub freezemon_ProcessTimer($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
#RemoveInternalTimer($hash);
|
||||
|
||||
my $now = gettimeofday();
|
||||
my $freeze = $now - $hash->{helper}{TIMER};
|
||||
|
||||
|
||||
#Check Freezes
|
||||
if ($freeze > AttrVal($name, "fm_freezeThreshold",1))
|
||||
{
|
||||
|
||||
my $dev = $hash->{helper}{apptime};
|
||||
my $guys = "";
|
||||
$dev //= "";
|
||||
my $start = strftime("%H:%M:%S",localtime($hash->{helper}{TIMER}));
|
||||
my $end = strftime("%H:%M:%S",localtime($now));
|
||||
$freeze = int($freeze * 1000) / 1000;
|
||||
|
||||
# Find the internal timers that are still in the hash
|
||||
my @olddev = split (" ",$dev);
|
||||
my @newdev = split (" ",freezemon_apptime());
|
||||
|
||||
my %nd = map { $_ => 1 } @newdev
|
||||
;
|
||||
foreach my $d (@olddev) {
|
||||
if (!exists($nd{$d})) {
|
||||
my @a = split ("-",$d);
|
||||
$guys .= $a[1]." ";
|
||||
}
|
||||
}
|
||||
|
||||
$dev = $guys;
|
||||
$dev =~ s/^\s+|\s+$//g;
|
||||
if ($dev eq "") {
|
||||
$dev = "no bad guy found :-(";
|
||||
}
|
||||
# Build hash with 20 last freezes
|
||||
my @freezes = ();
|
||||
push @freezes, split(",", ReadingsVal($name,".fm_freezes",undef));
|
||||
push @freezes, strftime("%Y-%m-%d",localtime).": s:$start e:$end f:$freeze d:$dev";
|
||||
while (keys @freezes > 20) {
|
||||
shift @freezes;
|
||||
}
|
||||
my $freezelist = join(",",@freezes);
|
||||
|
||||
# determine relevant loglevel
|
||||
my $loglevel = 1;
|
||||
my %params = map{split /\:/, $_}(split /\ /, AttrVal($name, "fm_log", ""));
|
||||
foreach my $param (reverse sort {$a <=> $b} keys %params) {
|
||||
if ($freeze > $param) {
|
||||
$loglevel = $params{$param};
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
Log3 $name, $loglevel, strftime("FreezeMon: $name possible freeze starting at %H:%M:%S, delay is $freeze possibly caused by $dev", localtime($hash->{helper}{TIMER}));
|
||||
my $fcDay = ReadingsVal($name,"fcDay",0)+1;
|
||||
my $ftDay = ReadingsVal($name,"ftDay",0)+$freeze;
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate($hash, ".fm_freezes", $freezelist, 0);
|
||||
readingsBulkUpdate($hash, "state", "s:$start e:$end f:$freeze d:$dev");
|
||||
readingsBulkUpdate($hash, "freezeTime", $freeze);
|
||||
readingsBulkUpdate($hash, "fcDay", $fcDay);
|
||||
readingsBulkUpdate($hash, "ftDay", $ftDay);
|
||||
readingsBulkUpdate($hash, "freezeDevice",$dev);
|
||||
readingsEndUpdate($hash,1);
|
||||
}
|
||||
|
||||
# ---- Some stuff not required every second
|
||||
$hash->{helper}{intCount} //= 0;
|
||||
$hash->{helper}{intCount} += 1;
|
||||
if ($hash->{helper}{intCount} >= 60) {
|
||||
$hash->{helper}{intCount} = 0;
|
||||
|
||||
#Update dayLast readings if we have a new day
|
||||
my $last = ReadingsVal($name,".lastDay","");
|
||||
my $dnow = strftime("%Y-%m-%d",localtime);
|
||||
if ($last eq "") {
|
||||
readingsSingleUpdate($hash, ".lastDay", $dnow,0);
|
||||
}
|
||||
elsif ($dnow gt $last)
|
||||
{
|
||||
my $fcDay = ReadingsVal($name,"fcDay",0);
|
||||
my $ftDay = ReadingsVal($name,"ftDay",0);
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate($hash, "fcDayLast", $fcDay);
|
||||
readingsBulkUpdate($hash, "ftDayLast", $ftDay);
|
||||
readingsBulkUpdate($hash, "fcDay", 0);
|
||||
readingsBulkUpdate($hash, "ftDay", 0);
|
||||
readingsBulkUpdate($hash, ".lastDay", $dnow, 0);
|
||||
readingsEndUpdate($hash,1);
|
||||
}
|
||||
|
||||
if (AttrVal($name,"fm_forceApptime",0) ==1 and !defined($cmds{"apptime"})) {
|
||||
fhem("apptime",1);
|
||||
}
|
||||
}
|
||||
# start next timer
|
||||
$hash->{helper}{fn} = "";
|
||||
$hash->{helper}{apptime} = freezemon_apptime();
|
||||
$hash->{helper}{TIMER} = int($now) + 1;
|
||||
InternalTimer($hash->{helper}{TIMER}, 'freezemon_ProcessTimer', $hash, 0);
|
||||
freezemon_start($hash);
|
||||
}
|
||||
###################################
|
||||
sub freezemon_Set($@)
|
||||
{
|
||||
my ( $hash, $name, $cmd, @args ) = @_;
|
||||
sub freezemon_ProcessTimer($) {
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
return "\"set $name\" needs at least one argument" unless(defined($cmd));
|
||||
#RemoveInternalTimer($hash);
|
||||
|
||||
if($cmd eq "inactive") {
|
||||
RemoveInternalTimer($hash);
|
||||
readingsSingleUpdate($hash, "state", "inactive",1);
|
||||
$hash->{helper}{DISABLED} = 1;
|
||||
}
|
||||
elsif($cmd eq "active") {
|
||||
if (IsDisabled($name)) {
|
||||
freezemon_start($hash);
|
||||
} else {
|
||||
return "Freezemon $name is already active";
|
||||
}
|
||||
}
|
||||
elsif($cmd eq "clear") {
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate($hash, "fcDayLast", 0, 1);
|
||||
readingsBulkUpdate($hash, "ftDayLast", 0, 1);
|
||||
readingsBulkUpdate($hash, "fcDay", 0, 1);
|
||||
readingsBulkUpdate($hash, "ftDay", 0, 1);
|
||||
readingsBulkUpdate($hash, ".lastDay", "", 1);
|
||||
readingsBulkUpdate($hash, ".fm_freezes", "", 1);
|
||||
if (!IsDisabled($name)) {
|
||||
readingsBulkUpdate($hash, "state", "initialized", 1);
|
||||
}
|
||||
readingsBulkUpdate($hash, "freezeTime", 0, 1);
|
||||
readingsBulkUpdate($hash, "freezeDevice","", 1);
|
||||
readingsEndUpdate($hash,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Unknown argument $cmd, choose one of active:noArg inactive:noArg clear:noArg";
|
||||
}
|
||||
return undef;
|
||||
my $now = gettimeofday();
|
||||
my $freeze = $now - $hash->{helper}{TIMER};
|
||||
|
||||
#Check Freezes
|
||||
if ( $freeze > AttrVal( $name, "fm_freezeThreshold", 1 ) ) {
|
||||
|
||||
my $dev = $hash->{helper}{apptime};
|
||||
my $guys = "";
|
||||
$dev //= "";
|
||||
my $start = strftime( "%H:%M:%S", localtime( $hash->{helper}{TIMER} ) );
|
||||
my $end = strftime( "%H:%M:%S", localtime($now) );
|
||||
$freeze = int( $freeze * 1000 ) / 1000;
|
||||
|
||||
# Find the internal timers that are still in the hash
|
||||
my @olddev = split( " ", $dev );
|
||||
my @newdev = split( " ", freezemon_apptime() );
|
||||
|
||||
my %nd = map { $_ => 1 } @newdev;
|
||||
foreach my $d (@olddev) {
|
||||
if ( !exists( $nd{$d} ) ) {
|
||||
my @a = split( "-", $d );
|
||||
$guys .= $a[1] . " ";
|
||||
}
|
||||
}
|
||||
|
||||
$dev = $guys;
|
||||
$dev =~ s/^\s+|\s+$//g;
|
||||
my $exists = undef;
|
||||
|
||||
if ( $dev eq "" ) {
|
||||
$dev = "no bad guy found :-(";
|
||||
$exists = 1;
|
||||
}
|
||||
else {
|
||||
#check ignorDev
|
||||
my $imode = "off";
|
||||
my %devs = map { split /\:/, $_ } ( split /\ /, $dev );
|
||||
my @idevs = split( ",", AttrVal( $name, "fm_ignoreDev", "" ) );
|
||||
my %id = map { $_ => 1 } @idevs;
|
||||
|
||||
if ( AttrVal( $name, "fm_ignoreDev", undef ) ) {
|
||||
$imode = AttrVal( $name, "fm_ignoreMode", "all" );
|
||||
}
|
||||
|
||||
#In "all" mode all found devices have to be in ignoreDevs (i.e. we're done if one is not in ignoreDev
|
||||
if ( $imode eq "all" ) {
|
||||
foreach my $d ( values %devs ) {
|
||||
if ( !exists( $id{$d} ) ) {
|
||||
Log3 $name, 5, "FreezeMon $name logging $dev in $imode mode, because $d is not ignored";
|
||||
$exists = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#In "single" mode a single found device has to be in ignoreDevs (i.e. we're done if one is in ignoreDev
|
||||
elsif ( $imode eq "single" ) {
|
||||
$exists = 1;
|
||||
foreach my $d ( values %devs ) {
|
||||
if ( exists( $id{$d} ) ) {
|
||||
Log3 $name, 5, "FreezeMon $name ignoring $dev in $imode mode, because $d is ignored";
|
||||
$exists = undef;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$exists = 1;
|
||||
}
|
||||
|
||||
#format output
|
||||
$dev =~ s/\:/\(/g;
|
||||
$dev =~ s/\s/\) /g;
|
||||
$dev .= ")";
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
|
||||
# Build hash with 20 last freezes
|
||||
my @freezes = ();
|
||||
push @freezes, split( ",", ReadingsVal( $name, ".fm_freezes", undef ) );
|
||||
push @freezes, strftime( "%Y-%m-%d", localtime ) . ": s:$start e:$end f:$freeze d:$dev";
|
||||
|
||||
#while (keys @freezes > 20) { #problem with older Perl versions
|
||||
while ( scalar(@freezes) > 20 ) {
|
||||
shift @freezes;
|
||||
}
|
||||
my $freezelist = join( ",", @freezes );
|
||||
|
||||
# determine relevant loglevel
|
||||
my $loglevel = 1;
|
||||
my %params = map { split /\:/, $_ } ( split /\ /, AttrVal( $name, "fm_log", "" ) );
|
||||
foreach my $param ( reverse sort { $a <=> $b } keys %params ) {
|
||||
if ( $freeze > $param ) {
|
||||
$loglevel = $params{$param};
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
Log3 $name, $loglevel,
|
||||
strftime(
|
||||
"FreezeMon: $name possible freeze starting at %H:%M:%S, delay is $freeze possibly caused by $dev",
|
||||
localtime( $hash->{helper}{TIMER} ) );
|
||||
my $fcDay = ReadingsVal( $name, "fcDay", 0 ) + 1;
|
||||
my $ftDay = ReadingsVal( $name, "ftDay", 0 ) + $freeze;
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate( $hash, ".fm_freezes", $freezelist, 0 );
|
||||
readingsBulkUpdate( $hash, "state", "s:$start e:$end f:$freeze d:$dev" );
|
||||
readingsBulkUpdate( $hash, "freezeTime", $freeze );
|
||||
readingsBulkUpdate( $hash, "fcDay", $fcDay );
|
||||
readingsBulkUpdate( $hash, "ftDay", $ftDay );
|
||||
readingsBulkUpdate( $hash, "freezeDevice", $dev );
|
||||
readingsEndUpdate( $hash, 1 );
|
||||
}
|
||||
else {
|
||||
Log3 $name, 5, "Freezemon: $name - $dev was ignored";
|
||||
}
|
||||
}
|
||||
|
||||
# ---- Some stuff not required every second
|
||||
$hash->{helper}{intCount} //= 0;
|
||||
$hash->{helper}{intCount} += 1;
|
||||
if ( $hash->{helper}{intCount} >= 60 ) {
|
||||
$hash->{helper}{intCount} = 0;
|
||||
|
||||
#Update dayLast readings if we have a new day
|
||||
my $last = ReadingsVal( $name, ".lastDay", "" );
|
||||
my $dnow = strftime( "%Y-%m-%d", localtime );
|
||||
if ( $last eq "" ) {
|
||||
readingsSingleUpdate( $hash, ".lastDay", $dnow, 0 );
|
||||
}
|
||||
elsif ( $dnow gt $last ) {
|
||||
my $fcDay = ReadingsVal( $name, "fcDay", 0 );
|
||||
my $ftDay = ReadingsVal( $name, "ftDay", 0 );
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate( $hash, "fcDayLast", $fcDay );
|
||||
readingsBulkUpdate( $hash, "ftDayLast", $ftDay );
|
||||
readingsBulkUpdate( $hash, "fcDay", 0 );
|
||||
readingsBulkUpdate( $hash, "ftDay", 0 );
|
||||
readingsBulkUpdate( $hash, ".lastDay", $dnow, 0 );
|
||||
readingsEndUpdate( $hash, 1 );
|
||||
}
|
||||
|
||||
if ( AttrVal( $name, "fm_forceApptime", 0 ) == 1
|
||||
and !defined( $cmds{"apptime"} ) )
|
||||
{
|
||||
fhem( "apptime", 1 );
|
||||
}
|
||||
}
|
||||
|
||||
# start next timer
|
||||
$hash->{helper}{fn} = "";
|
||||
$hash->{helper}{apptime} = freezemon_apptime();
|
||||
$hash->{helper}{TIMER} = int($now) + 1;
|
||||
InternalTimer( $hash->{helper}{TIMER}, 'freezemon_ProcessTimer', $hash, 0 );
|
||||
}
|
||||
###################################
|
||||
sub freezemon_Set($@) {
|
||||
my ( $hash, $name, $cmd, @args ) = @_;
|
||||
|
||||
return "\"set $name\" needs at least one argument" unless ( defined($cmd) );
|
||||
|
||||
if ( $cmd eq "inactive" ) {
|
||||
RemoveInternalTimer($hash);
|
||||
readingsSingleUpdate( $hash, "state", "inactive", 1 );
|
||||
$hash->{helper}{DISABLED} = 1;
|
||||
}
|
||||
elsif ( $cmd eq "active" ) {
|
||||
if ( IsDisabled($name) ) {
|
||||
freezemon_start($hash);
|
||||
}
|
||||
else {
|
||||
return "Freezemon $name is already active";
|
||||
}
|
||||
}
|
||||
elsif ( $cmd eq "clear" ) {
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate( $hash, "fcDayLast", 0, 1 );
|
||||
readingsBulkUpdate( $hash, "ftDayLast", 0, 1 );
|
||||
readingsBulkUpdate( $hash, "fcDay", 0, 1 );
|
||||
readingsBulkUpdate( $hash, "ftDay", 0, 1 );
|
||||
readingsBulkUpdate( $hash, ".lastDay", "", 1 );
|
||||
readingsBulkUpdate( $hash, ".fm_freezes", "", 1 );
|
||||
if ( !IsDisabled($name) ) {
|
||||
readingsBulkUpdate( $hash, "state", "initialized", 1 );
|
||||
}
|
||||
readingsBulkUpdate( $hash, "freezeTime", 0, 1 );
|
||||
readingsBulkUpdate( $hash, "freezeDevice", "", 1 );
|
||||
readingsEndUpdate( $hash, 1 );
|
||||
}
|
||||
else {
|
||||
return "Unknown argument $cmd, choose one of active:noArg inactive:noArg clear:noArg";
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
###################################
|
||||
@ -309,37 +363,35 @@ sub freezemon_Get($@) {
|
||||
my ( $hash, @a ) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $state = $hash->{STATE};
|
||||
my $ret = "";
|
||||
my $ret = "";
|
||||
return "No Argument given" if ( !defined( $a[1] ) );
|
||||
|
||||
Log3 $name, 5,
|
||||
"freezemon $name: called function freezemon_Get() with " . Dumper(@a);
|
||||
Log3 $name, 5, "freezemon $name: called function freezemon_Get() with " . Dumper(@a);
|
||||
|
||||
my $usage =
|
||||
"Unknown argument " . $a[1] . ", choose one of freeze:noArg";
|
||||
my $usage = "Unknown argument " . $a[1] . ", choose one of freeze:noArg";
|
||||
my $error = undef;
|
||||
|
||||
# Get freeze entries
|
||||
if ( $a[1] eq "freeze" ) {
|
||||
my @colors = ("red","yellow","green","white","gray");
|
||||
my @freezes = split(",", ReadingsVal($name,".fm_freezes",""));
|
||||
foreach (@freezes) {
|
||||
my $loglevel = 1;
|
||||
my $freeze = $_;
|
||||
if ($freeze =~ /f:(.*)d:/) {
|
||||
$freeze = $1;
|
||||
}
|
||||
my %params = map{split /\:/, $_}(split /\ /, AttrVal($name, "fm_log", ""));
|
||||
foreach my $param (reverse sort {$a <=> $b} keys %params) {
|
||||
if ($freeze > $param) {
|
||||
$loglevel = $params{$param};
|
||||
last;
|
||||
}
|
||||
}
|
||||
$_ =~s/(?<=.{160}).{1,}$/.../;
|
||||
$ret .= "<font color='$colors[$loglevel-1]'><b>".$loglevel."</b></font> - ".$_."<br>";
|
||||
}
|
||||
return $ret;
|
||||
my @colors = ( "red", "yellow", "green", "white", "gray" );
|
||||
my @freezes = split( ",", ReadingsVal( $name, ".fm_freezes", "" ) );
|
||||
foreach (@freezes) {
|
||||
my $loglevel = 1;
|
||||
my $freeze = $_;
|
||||
if ( $freeze =~ /f:(.*)d:/ ) {
|
||||
$freeze = $1;
|
||||
}
|
||||
my %params = map { split /\:/, $_ } ( split /\ /, AttrVal( $name, "fm_log", "" ) );
|
||||
foreach my $param ( reverse sort { $a <=> $b } keys %params ) {
|
||||
if ( $freeze > $param ) {
|
||||
$loglevel = $params{$param};
|
||||
last;
|
||||
}
|
||||
}
|
||||
$_ =~ s/(?<=.{160}).{1,}$/.../;
|
||||
$ret .= "<font color='$colors[$loglevel-1]'><b>" . $loglevel . "</b></font> - " . $_ . "<br>";
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
# return usage hint
|
||||
@ -352,149 +404,174 @@ sub freezemon_Get($@) {
|
||||
sub freezemon_Attr($) {
|
||||
|
||||
my ( $cmd, $name, $aName, $aVal ) = @_;
|
||||
my $hash = $defs{$name};
|
||||
my $hash = $defs{$name};
|
||||
|
||||
# $cmd can be "del" or "set"
|
||||
# $name is device name
|
||||
# aName and aVal are Attribute name and value
|
||||
Log3 $name, 3, "$cmd $aName $aVal";
|
||||
Log3 $name, 3, "$cmd $aName $aVal";
|
||||
if ( $cmd eq "set" ) {
|
||||
|
||||
|
||||
if ( $aName eq "fm_forceApptime" ) {
|
||||
if ( $aVal > 1 or $aVal < 0 ) {
|
||||
Log3 $name, 3,
|
||||
"$name: $aName is either 0 or 1: $aVal";
|
||||
return "Attribute " . $aName
|
||||
. " is either 0 or 1";
|
||||
Log3 $name, 3, "$name: $aName is either 0 or 1: $aVal";
|
||||
return "Attribute " . $aName . " is either 0 or 1";
|
||||
}
|
||||
}
|
||||
if ( $aName eq "fm_freezeThreshold" ) {
|
||||
if (!looks_like_number($aVal) ) {
|
||||
return "Attribute " . $aName. " has to be a number (seconds) ";
|
||||
}
|
||||
}
|
||||
if ( $aName eq "fm_freezeThreshold" ) {
|
||||
if ( $aName eq "fm_freezeThreshold" ) {
|
||||
if ( !looks_like_number($aVal) ) {
|
||||
return "Attribute " . $aName . " has to be a number (seconds) ";
|
||||
}
|
||||
}
|
||||
if ( $aName eq "fm_freezeThreshold" ) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( $aName eq "disable" ) {
|
||||
if ($aVal == 1) {
|
||||
RemoveInternalTimer($hash);
|
||||
readingsSingleUpdate($hash, "state", "inactive",1);
|
||||
$hash->{helper}{DISABLED} = 1;
|
||||
}
|
||||
elsif ($aVal == 0) {
|
||||
freezemon_start($hash);
|
||||
}
|
||||
if ( $aName eq "disable" ) {
|
||||
if ( $aVal == 1 ) {
|
||||
RemoveInternalTimer($hash);
|
||||
readingsSingleUpdate( $hash, "state", "inactive", 1 );
|
||||
$hash->{helper}{DISABLED} = 1;
|
||||
}
|
||||
elsif ( $aVal == 0 ) {
|
||||
freezemon_start($hash);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
elsif ( $cmd eq "del" ) {
|
||||
if ( $aName eq "disable" ) {
|
||||
freezemon_start($hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ( $cmd eq "del" ) {
|
||||
if ( $aName eq "disable" ) {
|
||||
freezemon_start($hash);
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
|
||||
}
|
||||
|
||||
|
||||
###################################
|
||||
# Helper Functions #
|
||||
###################################
|
||||
|
||||
###################################
|
||||
sub freezemon_start($) {
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
readingsSingleUpdate($hash, "state", "initialized",0) if(exists($hash->{helper}{DISABLED}) and $hash->{helper}{DISABLED} == 1);
|
||||
$hash->{helper}{DISABLED} = 0;
|
||||
my $next = int(gettimeofday()) + 1;
|
||||
$hash->{helper}{TIMER} = $next;
|
||||
InternalTimer($next, 'freezemon_ProcessTimer', $hash, 0);
|
||||
Log3 $name, 2, "FreezeMon: $name ready to watch out for delays greater than ".AttrVal($name, "fm_freezeThreshold",1)." second(s)";
|
||||
readingsSingleUpdate( $hash, "state", "initialized", 0 )
|
||||
if ( exists( $hash->{helper}{DISABLED} )
|
||||
and $hash->{helper}{DISABLED} == 1 );
|
||||
$hash->{helper}{DISABLED} = 0;
|
||||
my $next = int( gettimeofday() ) + 1;
|
||||
$hash->{helper}{TIMER} = $next;
|
||||
InternalTimer( $next, 'freezemon_ProcessTimer', $hash, 0 );
|
||||
Log3 $name, 2,
|
||||
"FreezeMon: $name ready to watch out for delays greater than "
|
||||
. AttrVal( $name, "fm_freezeThreshold", 1 )
|
||||
. " second(s)";
|
||||
}
|
||||
|
||||
###################################
|
||||
sub freezemon_apptime() {
|
||||
my @intAtKeys = keys(%intAt);
|
||||
my $now = gettimeofday();
|
||||
my $minCoverExec = 10; # Let's see if we can find more if we look ahead further
|
||||
my $minCoverWait = 0.00;
|
||||
my $ret = "";
|
||||
my @intAtKeys = keys(%intAt);
|
||||
my $now = gettimeofday();
|
||||
my $minCoverExec = 10; # Let's see if we can find more if we look ahead further
|
||||
my $minCoverWait = 0.00;
|
||||
my $ret = "";
|
||||
|
||||
my @intAtSort = (sort {$intAt{$a}{TRIGGERTIME} <=>
|
||||
$intAt{$b}{TRIGGERTIME} }
|
||||
(grep {($intAt{$_}->{TRIGGERTIME}-$now) <= $minCoverExec}
|
||||
@intAtKeys)); # get the timers to execute due to timeout and sort ascending by time
|
||||
my ($fn,$tim,$cv,$fnname,$arg, $shortarg);
|
||||
my @intAtSort =
|
||||
( sort { $intAt{$a}{TRIGGERTIME} <=> $intAt{$b}{TRIGGERTIME} }
|
||||
( grep { ( $intAt{$_}->{TRIGGERTIME} - $now ) <= $minCoverExec } @intAtKeys ) )
|
||||
; # get the timers to execute due to timeout and sort ascending by time
|
||||
my ( $fn, $tim, $cv, $fnname, $arg, $shortarg );
|
||||
|
||||
foreach my $i (@intAtSort) {
|
||||
$tim = $intAt{$i}{TRIGGERTIME};
|
||||
if ($tim - gettimeofday() > $minCoverWait) {
|
||||
#next;
|
||||
foreach my $i (@intAtSort) {
|
||||
$tim = $intAt{$i}{TRIGGERTIME};
|
||||
if ( $tim - gettimeofday() > $minCoverWait ) {
|
||||
|
||||
#next;
|
||||
}
|
||||
|
||||
if ( $intAt{$i}{FN} eq "freezemon_ProcessTimer" ) {
|
||||
next;
|
||||
}
|
||||
|
||||
$fn = $intAt{$i}{FN};
|
||||
|
||||
if ( ref($fn) ne "" ) {
|
||||
$cv = svref_2object($fn);
|
||||
$fnname = $cv->GV->NAME;
|
||||
$ret .= $intAt{$i}{TRIGGERTIME} . "-" . $fnname;
|
||||
Log3 undef, 3, "Freezemon: Reference found: " . ref($fn) . "/$fnname/$fn";
|
||||
}
|
||||
else {
|
||||
$ret .= $intAt{$i}{TRIGGERTIME} . "-" . $fn;
|
||||
}
|
||||
$arg = $intAt{$i}{ARG};
|
||||
|
||||
$shortarg = ( defined($arg) ? $arg : "" );
|
||||
if ( ref($shortarg) eq "HASH" ) {
|
||||
if ( !defined( $shortarg->{NAME} ) ) {
|
||||
if ( $fn eq "BlockingKill" ) {
|
||||
$shortarg = $shortarg->{abortArg}{NAME};
|
||||
}
|
||||
elsif ( $fn eq "HttpUtils_Err" ) {
|
||||
$shortarg = $shortarg->{hash}{hash}{NAME};
|
||||
}
|
||||
else {
|
||||
Log3 undef, 5, "Freezemon: found something without a name $fn" . Dumper($shortarg);
|
||||
$shortarg = "N/A";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$shortarg = $shortarg->{NAME};
|
||||
}
|
||||
}
|
||||
elsif ( ref($shortarg) eq "REF" ) {
|
||||
if ( $fn eq "DOIF_TimerTrigger" ) {
|
||||
my $deref = ${$arg}; #seems like $arg is a reference to a scalar which in turm is a reference to a hash
|
||||
$shortarg = $deref->{'hash'}{NAME}; #at least in DOIF_TimerTrigger
|
||||
}
|
||||
else {
|
||||
Log3 undef, 5, "Freezemon: found a REF $fn " . Dumper( ${$arg} );
|
||||
}
|
||||
}
|
||||
else {
|
||||
#Log3 undef, 3, "Freezemon: found something that's not a HASH $fn ".ref($shortarg)." ".Dumper($shortarg);
|
||||
$shortarg = "N/A";
|
||||
}
|
||||
if ( !$shortarg ) {
|
||||
Log3 undef, 5, "Freezemon: something went wrong $fn " . Dumper($arg);
|
||||
$shortarg = "";
|
||||
}
|
||||
else {
|
||||
( $shortarg, undef ) = split( /:|;/, $shortarg, 2 );
|
||||
}
|
||||
$ret .= ":" . $shortarg . " ";
|
||||
}
|
||||
if ($intAt{$i}{FN} eq "freezemon_ProcessTimer") {
|
||||
next;
|
||||
}
|
||||
if (%prioQueues) {
|
||||
my $nice = minNum( keys %prioQueues );
|
||||
my $entry = shift( @{ $prioQueues{$nice} } );
|
||||
Log3 undef, 3, "Freezemon: found a prioQueue" . Dumper($entry);
|
||||
$cv = svref_2object( $entry->{fn} );
|
||||
$fnname = $cv->GV->NAME;
|
||||
$ret .= ":" . $shortarg;
|
||||
$shortarg = ( defined( $entry->{arg} ) ? $entry->{arg} : "" );
|
||||
if ( ref($shortarg) eq "HASH" ) {
|
||||
|
||||
$fn = $intAt{$i}{FN};
|
||||
if (ref($fn) ne "") {
|
||||
$cv = svref_2object($fn);
|
||||
$fnname = $cv->GV->NAME;
|
||||
$ret .= $intAt{$i}{TRIGGERTIME}."-".$fnname;
|
||||
Log3 undef, 3, "Freezemon: Reference found: ".ref($fn)."/$fnname/$fn";
|
||||
if ( !defined( $shortarg->{NAME} ) ) {
|
||||
$shortarg = "N/A";
|
||||
}
|
||||
else {
|
||||
$shortarg = $shortarg->{NAME};
|
||||
}
|
||||
}
|
||||
( $shortarg, undef ) = split( /:|;/, $shortarg, 2 );
|
||||
$ret .= ":" . $shortarg . " ";
|
||||
}
|
||||
else {
|
||||
$ret .= $intAt{$i}{TRIGGERTIME}."-".$fn;
|
||||
}
|
||||
$arg = $intAt{$i}{ARG};
|
||||
|
||||
|
||||
$shortarg = (defined($arg)?$arg:"");
|
||||
if (ref($shortarg) eq "HASH") {
|
||||
|
||||
if (!defined($shortarg->{NAME})) {
|
||||
$shortarg = "N/A";
|
||||
}
|
||||
else {
|
||||
$shortarg = $shortarg->{NAME};
|
||||
|
||||
if( $fn eq 'notify_Exec' || $fn eq "DOIF_TimerTrigger") {
|
||||
Log3 undef, 3, "Freezemon: found a DOIF or notify $fn".Dumper($arg);
|
||||
#my $events = deviceEvents($arg[1], AttrVal($arg[0]->{NAME}, "addStateEvent", 0));
|
||||
#$shortarg .= ">>".join( ',', @{$events})."<<";
|
||||
}
|
||||
}
|
||||
}
|
||||
($shortarg,undef) = split(/:|;/,$shortarg,2);
|
||||
$ret.= "(".$shortarg.") ";
|
||||
return $ret;
|
||||
}
|
||||
if(%prioQueues) {
|
||||
my $nice = minNum(keys %prioQueues);
|
||||
my $entry = shift(@{$prioQueues{$nice}});
|
||||
|
||||
$cv = svref_2object($entry->{fn});
|
||||
$fnname = $cv->GV->NAME;
|
||||
$ret .= $fnname;
|
||||
$shortarg = (defined($entry->{arg})?$entry->{arg}:"");
|
||||
if (ref($shortarg) eq "HASH") {
|
||||
if (!defined($shortarg->{NAME})) {
|
||||
$shortarg = "N/A";
|
||||
}
|
||||
else {
|
||||
$shortarg = $shortarg->{NAME};
|
||||
}
|
||||
}
|
||||
($shortarg,undef) = split(/:|;/,$shortarg,2);
|
||||
$ret.= "(".$shortarg.") ";
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
=pod
|
||||
=item helper
|
||||
@ -557,6 +634,12 @@ if(%prioQueues) {
|
||||
<ul>
|
||||
<li>fm_freezeThreshold: Value in seconds (Default: 1) - Only freezes longer than fm_freezeThreshold will be considered as a freeze</li>
|
||||
<li>fm_forceApptime: When FREEZEMON is active, apptime will automatically be started (if not yet active)</li>
|
||||
<li>fm_ignoreDev: list of comma separated Device names. If all devices possibly causing a freeze are in the list, the freeze will be ignored (not logged)</li>
|
||||
<li>fm_ignoreMode: takes the values off,single or all. If you have added devices to fm_ignoreDev then ignoreMode acts as follows: <br>
|
||||
all: A freeze will only be ignored, if all devices probably causing the freeze are part of the ignore list. This might result in more freezes being logged than expected.<br>
|
||||
single: A freeze will be ignored as soon as one device possibly causing the freeze is listed in the ignore list. With this setting you might miss freezes.<br>
|
||||
off: All freezes will be logged.<br>
|
||||
If the attribute is not set, while the ignore list is maintained, mode "all" will be used.</li>
|
||||
<li>fm_log: dynamic loglevel, takes a string like 10:1 5:2 1:3 , which means: freezes > 10 seconds will be logged with loglevel 1 , >5 seconds with loglevel 2 etc...</li>
|
||||
<li>disable: activate/deactivate freeze detection</li>
|
||||
</ul>
|
||||
@ -575,14 +658,14 @@ if(%prioQueues) {
|
||||
<h3>freezemon</h3>
|
||||
<div>
|
||||
<ul>
|
||||
FREEZEMON überwacht - ähnlich wie PERFMON mögliche Freezes, allerdings ist FREEZEMON ein echtes Modul und hat daher:<br>
|
||||
FREEZEMON überwacht - ähnlich wie PERFMON mögliche Freezes, allerdings ist FREEZEMON ein echtes Modul und hat daher:<br>
|
||||
<ul>
|
||||
<li>Readings - die geloggt werden können und damit viel einfacher ausgewertet werden können</li>
|
||||
<li>Readings - die geloggt werden können und damit viel einfacher ausgewertet werden können</li>
|
||||
<li>Attribute - mit denen das Verhalten von freezemon beeinflusst werden kann</li>
|
||||
<li>zusätzliche Funktionalität - die versucht das den Freeze verursachende Device zu identifizieren</li>
|
||||
<li>zusätzliche Funktionalität - die versucht das den Freeze verursachende Device zu identifizieren</li>
|
||||
</ul>
|
||||
Ich würde empfehlen, PERFMON zu deaktivieren, wenn FREEZEMON aktiv ist, da beide auf die selbe Art Freezes erkennen und dann nur alles doppelt kommt.
|
||||
<b>Bitte beachten!</b> FREEZEMON versucht nur intelligent zu erraten, welches Device einen freeze verursacht haben könnte (basierend auf den Timern die laufen sollten). Es gibt eine Menge anderer Faktoren (intern oder extern) die einen Freeze verursachen können. FREEZEMON ersetzt keine detaillierte Analyse. Das Modul versucht nur Hinweise zu geben, was optimiert werden könnte.<br><br>
|
||||
Ich würde empfehlen, PERFMON zu deaktivieren, wenn FREEZEMON aktiv ist, da beide auf die selbe Art Freezes erkennen und dann nur alles doppelt kommt.
|
||||
<b>Bitte beachten!</b> FREEZEMON versucht nur intelligent zu erraten, welches Device einen freeze verursacht haben könnte (basierend auf den Timern die laufen sollten). Es gibt eine Menge anderer Faktoren (intern oder extern) die einen Freeze verursachen können. FREEZEMON ersetzt keine detaillierte Analyse. Das Modul versucht nur Hinweise zu geben, was optimiert werden könnte.<br><br>
|
||||
<br>
|
||||
<br>
|
||||
<a name="freezemonDefine"></a>
|
||||
@ -599,14 +682,14 @@ if(%prioQueues) {
|
||||
<ul>
|
||||
<li>inactive: deaktiviert das Device (identisch zum Attribut "disable", aber ohne die Notwendigkeit su "saven".</li>
|
||||
<li>active: reaktiviert das Device nachdem es auf inactive gesetzt wurde</li>
|
||||
<li>clear: Löscht alle readings (inklusive der Liste der letzten 20 Freezes).</li>
|
||||
<li>clear: Löscht alle readings (inklusive der Liste der letzten 20 Freezes).</li>
|
||||
</ul>
|
||||
|
||||
</ul>
|
||||
<a name="freezemonGet"></a>
|
||||
<b>Get</b>
|
||||
<ul>
|
||||
freeze: gibt die letzten 20 freezes zurück (in Kompakter Darstellung, wie im state) - Dies dient einem schnellen Überblick, für detailliertere Auswertungen empfehle ich die Daten zu loggen.<br><br>
|
||||
freeze: gibt die letzten 20 freezes zurück (in Kompakter Darstellung, wie im state) - Dies dient einem schnellen Überblick, für detailliertere Auswertungen empfehle ich die Daten zu loggen.<br><br>
|
||||
</ul>
|
||||
|
||||
<a name="freezemonReadings"></a>
|
||||
@ -614,7 +697,7 @@ if(%prioQueues) {
|
||||
<ul>
|
||||
<ul>
|
||||
<li>freezeTime: Dauer des Freezes</li>
|
||||
<li>freezeDevice: Liste von möglicherweise den Freeze auslösenden Funktionen(Devices)</li>
|
||||
<li>freezeDevice: Liste von möglicherweise den Freeze auslösenden Funktionen(Devices)</li>
|
||||
<li>fcDay: kumulierte Anzahl der Freezes pro Tag</li>
|
||||
<li>ftDay: kumulierte Dauer der Freezes pro Tag </li>
|
||||
<li>fcDayLast: speichert die kumulierte Anzahl der Freezes des vergangenen Tages (um tageweise plots zu erstellen)</li>
|
||||
@ -627,8 +710,14 @@ if(%prioQueues) {
|
||||
<b>Attribute</b>
|
||||
<ul>
|
||||
<ul>
|
||||
<li>fm_freezeThreshold: Wert in Sekunden (Default: 1) - Nur Freezes länger als fm_freezeThreshold werden als Freeze betrachtet </li>
|
||||
<li>fm_freezeThreshold: Wert in Sekunden (Default: 1) - Nur Freezes länger als fm_freezeThreshold werden als Freeze betrachtet </li>
|
||||
<li>fm_forceApptime: Wenn FREEZEMON aktiv ist wird automatisch apptime gestartet (falls nicht aktiv)</li>
|
||||
<li>fm_ignoreDev: Liste von Komma-getrennten Devices. Wenn einzelne möglicherweise einen Freeze verursachenden Device in dieser Liste sind, wird der Freeze ignoriert (nicht geloggt). Bitte das Attribut fm_ignoreMode beachten</li>
|
||||
<li>fm_ignoreMode: Kann die Werte off,single oder all annehmen. Wenn in fm_ignoreDev Devices angegeben sind wirken sich der ignoreMode wie folgt aus: <br>
|
||||
all: Ein Freeze wird nur dann ignoriert, wenn alle möglicherweise den Freeze verursachenden Devices in der Ignore-Liste enthalten sind. Dies führt unter Umständen dazu, dass mehr Freezes geloggt werden als erwartet.<br>
|
||||
single: Ein Freeze wird ignoriert, sobald ein möglicher Verursacher in der Ignorierliste enthalten ist. Dies führt möglicherweise dazu, dass Freezes übersehen werden.<br>
|
||||
off: Alle Freezes werden geloggt.<br>
|
||||
Sofern das Attribut nicht gesetzt ist, aber Ignore-Devices angegeben sind, wird im Modus "all" ignoriert.</li>
|
||||
<li>fm_log: dynamischer Loglevel, nimmt einen String der Form 10:1 5:2 1:3 entgegen, was bedeutet: Freezes > 10 Sekunden werden mit Loglevel 1 geloggt, >5 Sekunden mit Loglevel 2 usw...</li>
|
||||
<li>disable: aktivieren/deaktivieren der Freeze-Erkennung</li>
|
||||
</ul>
|
||||
|
Loading…
x
Reference in New Issue
Block a user