##############################################
# $Id: 99_Checkstate.pm $
#
# Checks for timeout and battery stat.
#
# Default timeout are set in the devtype_timeout array below.
# You may set an individual timeout by setting an attribute "timeout".
# The timeout must be specified in seconds. 0 means that the device is not checked for timeout.
# Before you can do so define a user attribute to all devices:
# attr global userattr timeout .......
#
# Devices are checked for battery state of the Reading battery exist.
# To exclude an device from battery check, just set an user atriibute "ignore_battery" to any value.
# attr global userattr ignore_batter .....y
#
package main;
use strict;
use warnings;
use POSIX;
use English qw<$OS_ERROR>;
sub CommandCheckstate($$);
sub XmlEscape($);
my $hostname = "hersheeva";
my $subject = "";
# Default values for each device type:
my %devtype_timeout =
(
"CUL_FHTTK" => 30 * 60, # 30 Minutes, FHTTK report every 4 minutes
"CUL_WS" => 3 * 60*60, # 3 hours, CUL_WS report every 3 minutes
"FHT" => 1 * 60*60, # 1 hour, FHT report ....
"FS20" => 0, # no timeout
"TRX_WEATHER" => 10 * 60, # 10 Minutes, Oregon devices report every minute
"TRX_SECURITY" => 4 * 60*60, # 4 hours, TRX_SECURITY reports every hour
"TRX_LIGHT" => 0, # no timeout
"RFXX10REC" => 0, # no timeout
);
my $device_default_timeout = 1 * 60*60*24; # 1 Day
#####################################
sub
Checkstate_Initialize($$)
{
my %lhash = ( Fn=>"CommandCheckstate",
Hlp=>",check state of all devices if STATE is valid. Check for Battery change." );
$cmds{checkstate} = \%lhash;
}
#####################################
sub
XmlEscape($)
{
my $a = shift;
return "" if(!defined($a));
$a =~ s/\\\n/<br>/g; # Multi-line
$a =~ s/&/&/g;
$a =~ s/"/"/g;
$a =~ s/</</g;
$a =~ s/>/>/g;
# Not needed since we've gone UTF-8
# $a =~ s/([^ -~])/sprintf("&#%02x;", ord($1))/ge;
# Esacape characters 0-31, as they are not part of UTF-8
$a =~ s/(\x00-\x19)//g;
return $a;
}
#####################################
sub
CommandCheckstate($$)
{
my ($cl, $param) = @_;
#my $str = "<FHZINFO>\n";
my $str = "";
my $lt = "";
my $timeout_str = "";
my $battery_str = "";
delete($modules{""}) if(defined($modules{""}));
for my $d (sort { my $x = $modules{$defs{$a}{TYPE}}{ORDER}.$defs{$a}{TYPE} cmp
$modules{$defs{$b}{TYPE}}{ORDER}.$defs{$b}{TYPE};
$x = ($a cmp $b) if($x == 0); $x; } keys %defs) {
next if(IsIgnored($d));
my $p = $defs{$d};
my $t = $p->{TYPE};
my $a1 = XmlEscape($p->{STATE});
my $TIME_STRING = "TIME";
if (defined($p->{LASTIODev})) {
$TIME_STRING = $p->{LASTIODev}."_TIME";
}
my $state_time = "";
if (!defined($p->{$TIME_STRING})) {
my $latest_time = 0;
# Look into all Readings and get the latest Time_value:
my $hash = $defs{$d}{READINGS};
foreach my $n (sort keys %{$hash}) {
my $val = $hash->{$n};
if(ref($val)) {
# These are the READINGS:
my $tmp_value = $val->{VAL};
my $tmp_time = $val->{TIME};
my $tmp_time_num = time_str2num($tmp_time);
#Log 1,"ListReadings: val=$val nname=$n, value=$tmp_value, time=$tmp_time time_num=$tmp_time_
num";
if ($tmp_time_num > $latest_time) { $latest_time = $tmp_time_num; }
}
}
if ($latest_time != 0) { $state_time = FmtDateTime($latest_time); }
} else {
$state_time = $p->{$TIME_STRING};
}
next if ($state_time eq "");
#$str .= "<type=$t name=\"$d\" time=\"$state_time\" state=\"$a1\" \n";
my $time_now = time();
my $time_num = time_str2num($state_time);
my $time_diff = $time_now - $time_num;
my $device_timeout = $device_default_timeout;
if (exists $devtype_timeout{$t}) {
$device_timeout = $devtype_timeout{$t};
}
my $attr = AttrVal("$d","timeout", undef);
if (defined($attr)) {
#$str .= sprintf("\t%s\t attribute timeout %s\n", $d, $attr);
$device_timeout = $attr;
}
#
if ($device_timeout > 0 && $time_diff > $device_timeout) {
#Log 1,"Checkstate: -1- $d reported $state_time: \t'$p->{STATE}'";
if (defined ($p->{STATE})) {
if (! ($p->{STATE} =~ m/^\?.*/)) {
#Log 1,"Checkstate: $d reported $state_time";
$p->{STATE} = "? ".$p->{STATE};
$timeout_str .= sprintf("\t%s\t reported %s\n", $d, $state_time);
} else {
$timeout_str .= sprintf("\t%s\t again reported %s\n", $d, $state_time);
}
} else {
$timeout_str .= sprintf("\t%s\t reported %s\n", $d, $state_time);
}
#$str .= "!!!time_num=$time_num time_diff=$time_diff (devtimeout=$device_timeout)\n";
} else {
#$str .= " time_num=$time_num time_diff=$time_diff (devtimeout=$device_timeout)\n";
}
my $r = $p->{READINGS};
if($r) {
foreach my $c (sort keys %{$r}) {
my $h = $r->{$c};
next if(!defined($h->{VAL}) || !defined($h->{TIME}));
next if($c ne "battery");
my $ignore_battery_attr = AttrVal("$d","ignore_battery", undef);
next if (defined($ignore_battery_attr));
if($h->{VAL} eq "low") {
$battery_str .=
sprintf("\t%s\t reported %s\n", $d, $h->{TIME});
}
}
}
}
$subject = "FHEM ".$hostname.": ";
if ($timeout_str ne "") {
$str .= "\n- The following devices have timeouts:\n".$timeout_str;
$subject .= " <devices have timeouts>";
}
if ($battery_str ne "") {
$str .= "\n- The following devices have low battery:\n".$battery_str;
$subject .= " <battery low for devices>";
}
if ($str ne "") {
open( my $mailh, '|-', "mail -s '$subject' $email" )
or die( "Could not open pipe! $OS_ERROR" )
;
#print $mailh $str, "\n.\n";
print $mailh $str, "\n";
close $mailh;
}
return $str;
}
1;