################################################################ # # Copyright notice # # (c) 2013 Alexander Schulz # # This script is 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. # # This copyright notice MUST APPEAR in all copies of the script! # ################################################################ # # SSH support by PitpatV # ################################################################ # $Id$ # TODO: #fyi: #The path of #/sys/class/power_supply/{ac,usb,battery} #on a cubietruck, using armbian (Debian Stretch), Kernel 4.14.18-sunxi moved to #/sys/class/power_supply/axp20x-{ac,usb,battery} . #This makes the 42-SYSMON:power_* functions stop working here. #regards, #llutz package main; use strict; use warnings; use Scalar::Util qw(looks_like_number); use Blocking; use Data::Dumper; my $missingModulRemote; eval "use Net::Telnet;1" or $missingModulRemote .= "Net::Telnet "; my $VERSION = "2.3.7"; use constant { PERL_VERSION => "perl_version", DATE => "date", UPTIME => "uptime", UPTIME_TEXT => "uptime_text", STARTTIME_TEXT => "starttime_text", STARTTIME => "starttime", FHEMSTARTTIME_TEXT => "fhemstarttime_text", FHEMSTARTTIME => "fhemstarttime", FHEMUPTIME => "fhemuptime", FHEMUPTIME_TEXT => "fhemuptime_text", IDLETIME => "idletime", IDLETIME_TEXT => "idletime_text" }; use constant { CPU_CORE_CNT => "cpu_core_count", CPU_FREQ => "cpu_freq", CPU0_FREQ => "cpu0_freq", CPU1_FREQ => "cpu1_freq", CPU2_FREQ => "cpu2_freq", CPU3_FREQ => "cpu3_freq", CPU4_FREQ => "cpu4_freq", CPU5_FREQ => "cpu5_freq", CPU6_FREQ => "cpu6_freq", CPU7_FREQ => "cpu7_freq", CPU_BOGOMIPS => "cpu_bogomips", CPU_MODEL_NAME=>"cpu_model_name", CPU_TEMP => "cpu_temp", CPU0_TEMP => "cpu0_temp", CPU1_TEMP => "cpu1_temp", CPU2_TEMP => "cpu2_temp", CPU3_TEMP => "cpu3_temp", CPU4_TEMP => "cpu4_temp", CPU5_TEMP => "cpu5_temp", CPU6_TEMP => "cpu6_temp", CPU7_TEMP => "cpu7_temp", CPU_TEMP_AVG => "cpu_temp_avg", CPU0_TEMP_AVG => "cpu0_temp_avg", CPU1_TEMP_AVG => "cpu1_temp_avg", CPU2_TEMP_AVG => "cpu2_temp_avg", CPU3_TEMP_AVG => "cpu3_temp_avg", CPU4_TEMP_AVG => "cpu4_temp_avg", CPU5_TEMP_AVG => "cpu5_temp_avg", CPU6_TEMP_AVG => "cpu6_temp_avg", CPU7_TEMP_AVG => "cpu7_temp_avg", LOADAVG => "loadavg" }; use constant { RAM => "ram", SWAP => "swap" }; use constant { ETH0 => "eth0", WLAN0 => "wlan0", DIFF_SUFFIX => "_diff", SPEED_SUFFIX => "_speed", IP_SUFFIX => "_ip", IP6_SUFFIX => "_ip6", FB_WLAN_STATE => "wlan_state", FB_WLAN_GUEST_STATE => "wlan_guest_state", FB_INET_IP => "internet_ip", FB_INET_STATE => "internet_state", FB_N_TIME_CTRL => "night_time_ctrl", FB_NUM_NEW_MESSAGES => "num_new_messages", FB_FW_VERSION => "fw_version_info", FB_DECT_TEMP => "dect_temp", FB_DSL_RATE => "dsl_rate", FB_DSL_SYNCTIME => "dsl_synctime", FB_DSL_FEC_15 => "dsl_fec_15", FB_DSL_CRC_15 => "dsl_crc_15", }; use constant FS_PREFIX => "~ "; #use constant FS_PREFIX_N => "fs_"; my $DEFAULT_INTERVAL_BASE = 60; sub SYSMON_Initialize($) { my ($hash) = @_; SYSMON_Log($hash, 5, ""); $hash->{DefFn} = "SYSMON_Define"; $hash->{UndefFn} = "SYSMON_Undefine"; $hash->{GetFn} = "SYSMON_Get"; $hash->{SetFn} = "SYSMON_Set"; $hash->{AttrFn} = "SYSMON_Attr"; $hash->{NotifyFn} = "SYSMON_Notify"; $hash->{AttrList} = "filesystems network-interfaces user-defined disable:0,1 nonblocking:0,1 ". "telnet-time-out ". "user-fn2 user-fn ". "telnet-prompt-regx telnet-login-prompt-regx ". "exclude ". "ssh-params ". $readingFnAttributes; } ### attr NAME user-defined osUpdates:1440:Aktualisierungen:cat ./updates.txt [,:::] sub SYSMON_Define($$) { my ($hash, $def) = @_; SYSMON_Log($hash, 5, "$def"); my @a = split("[ \t][ \t]*", $def); return "Usage: define SYSMON [MODE[:[USER@]HOST][:PORT]] [M1 [M2 [M3 [M4]]]]" if(@a < 2); # define sysmon SYSMON local # define sysmon SYSMON local 1 1 1 10 # define sysmon SYSMON telnet:fritz.box # define sysmon SYSMON telnet:fritz.box:23 # define sysmon SYSMON telnet:fritz.box:23 10 10 10 60 # define sysmon SYSMON telnet:user@fritz.box:23 if(int(@a)>=3) { my @na = @a[2..scalar(@a)-1]; # wenn das erste Element nicht numerisch if(!($na[0] =~ /^\d+$/)) { # set mode/host/port my($mode, $host, $port) = split(/:/, $na[0]); $mode=lc($mode); # TODO SSH if(defined($mode)&&($mode eq 'local' || $mode eq 'telnet' || $mode eq 'ssh')) { $hash->{MODE} = $mode; delete($hash->{HOST}); delete($hash->{USER}); # erkennen, wenn User angegeben ist if($host) { my($user,$th) = split(/@/,$host); if(defined($th)) { $hash->{USER} = lc($user); $host = $th; } $hash->{HOST} = lc($host) if(defined($host)); # DefaultPort je nach Protokol if(!defined($port)) { $port = '23' if($mode eq 'telnet'); $port = '22' if($mode eq 'ssh'); } $hash->{PORT} = lc($port); } } else { return "unexpected mode. Use local, ssh or telnet only."; } shift @na; } else { $hash->{MODE}='local'; } SYSMON_setInterval($hash, @na); } else { $hash->{MODE}='local'; SYSMON_setInterval($hash, undef); } $hash->{NOTIFYDEV} = "global,TYPE=SYSMON"; #$hash->{STATE} = "Initialized"; $hash->{STATE} = "Loaded"; #$hash->{DEF_TIME} = time() unless defined($hash->{DEF_TIME}); #SYSMON_updateCurrentReadingsMap($hash); if($init_done) { RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$hash->{INTERVAL_BASE}, "SYSMON_Update", $hash, 0); } #$hash->{LOCAL} = 1; #SYSMON_Update($hash); #-> so nicht. hat im Startvorgang gelegentlich (oft) den Server 'aufgehaengt' #delete $hash->{LOCAL}; return undef; } sub SYSMON_Notify() { my ($hash,$dev) = @_; if( $dev->{NAME} eq "global" ) { if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) { #Log3($hash->{NAME},5,"SYSMON:DEBUG:> [$hash->{NAME}] notify for global ".Dumper(@{$dev->{CHANGED}})); # FHEM (re)Start RemoveInternalTimer($hash); InternalTimer(gettimeofday()+5+$hash->{INTERVAL_BASE}, "SYSMON_Update", $hash, 0); $hash->{STATE} = "Initialized"; } } } sub SYSMON_setInterval($@) { my ($hash, @a) = @_; my $interval = $DEFAULT_INTERVAL_BASE; $hash->{INTERVAL_BASE} = $interval; my $p1=1; my $p2=1; my $p3=1; my $p4=10; if(defined($a[0]) && int($a[0]) eq $a[0]) {$p1 = $a[0];} if(defined($a[1]) && int($a[1]) eq $a[1]) {$p2 = $a[1];} else {$p2 = $p1;} if(defined($a[2]) && int($a[2]) eq $a[2]) {$p3 = $a[2];} else {$p3 = $p1;} if(defined($a[3]) && int($a[3]) eq $a[3]) {$p4 = $a[3];} else {$p4 = $p1*10;} $hash->{INTERVAL_MULTIPLIERS} = $p1." ".$p2." ".$p3." ".$p4; } #my $cur_readings_map; => $hash->{helper}{cur_readings_map} sub SYSMON_updateCurrentReadingsMap($) { my ($hash) = @_; my $name = $hash->{NAME}; if( AttrVal($name, "disable", "") eq "1" ) { return undef; } my $rMap; # Map aktueller Namen erstellen # Feste Werte my $mode = $hash->{MODE};#AttrVal( $name, 'mode', 'local'); if($mode eq 'local'){ $rMap->{+PERL_VERSION} = "Perl Version"; } $rMap->{+DATE} = "Date"; $rMap->{+CPU_BOGOMIPS} = "BogoMIPS"; $rMap->{+CPU_MODEL_NAME} = "CPU model name"; if(SYSMON_isCPUFreqRPiBBB($hash)) { $rMap->{"cpu_freq"} = "CPU frequency"; $rMap->{"cpu0_freq"} = "CPU frequency"; $rMap->{"cpu_freq_stat"} = "CPU frequency stat"; $rMap->{"cpu0_freq_stat"} = "CPU frequency stat"; } foreach my $li (0..7) { if(SYSMON_isCPUXFreq($hash, $li)) { $rMap->{"cpu".$li."_freq"} = "CPU frequency (core $li)"; $rMap->{"cpu".$li."_freq_stat"} = "CPU frequency (core $li) stat"; } } if(SYSMON_isCPUTempRPi($hash) || SYSMON_isCPUTempBBB($hash) || SYSMON_isCPUTempFB($hash)) { #$rMap->{+CPU_TEMP} = "CPU Temperatur"; #$rMap->{"cpu_temp_avg"} = "Durchschnittliche CPU Temperatur"; $rMap->{+CPU_TEMP} = "CPU temperature"; $rMap->{+CPU_TEMP.'_stat'}= "CPU temperature stat"; #$rMap->{"cpu0_temp"} = "CPU temperature (core 0)"; $rMap->{"cpu_temp_avg"} = "Average CPU temperature"; #$rMap->{"cpu0_temp_avg"} = "Average CPU temperature (core 0)"; } foreach my $li (0..7) { if(SYSMON_isCPUTemp_X($hash, $li)) { $rMap->{"cpu".$li."_temp"} = "CPU temperature (core $li)"; $rMap->{"cpu".$li."_temp_avg"} = "Average CPU temperature (core $li)"; $rMap->{"cpu".$li."_temp_stat"} = "CPU temperature stat (core $li)"; } } $rMap->{+CPU_CORE_CNT} = "Number of CPU cores"; if(SYSMON_isSysPowerAc($hash)) { #$rMap->{"power_ac_online"} = "AC-Versorgung Status"; #$rMap->{"power_ac_present"} = "AC-Versorgung vorhanden"; #$rMap->{"power_ac_current"} = "AC-Versorgung Strom"; #$rMap->{"power_ac_voltage"} = "AC-Versorgung Spannung"; $rMap->{"power_ac_stat"} = "AC-Versorgung Info"; $rMap->{"power_ac_text"} = "AC-Versorgung Info"; } if(SYSMON_isSysPowerUsb($hash)) { #$rMap->{"power_usb_online"} = "USB-Versorgung Status"; #$rMap->{"power_usb_present"} = "USB-Versorgung vorhanden"; #$rMap->{"power_usb_current"} = "USB-Versorgung Strom"; #$rMap->{"power_usb_voltage"} = "USB-Versorgung Spannung"; $rMap->{"power_usb_stat"} = "USB-Versorgung Info"; $rMap->{"power_usb_text"} = "USB-Versorgung Info"; } if(SYSMON_isSysPowerBat($hash)) { #$rMap->{"power_battery_online"} = "Batterie-Versorgung Status"; #$rMap->{"power_battery_present"} = "Batterie-Versorgung vorhanden"; #$rMap->{"power_battery_current"} = "Batterie-Versorgung Strom"; #$rMap->{"power_battery_voltage"} = "Batterie-Versorgung Spannung"; $rMap->{"power_battery_stat"} = "Batterie-Versorgung Info"; $rMap->{"power_battery_text"} = "Batterie-Versorgung Info"; $rMap->{"power_battery_info"} = "Batterie-Versorgung Zusatzinfo"; } #$rMap->{"fhemuptime"} = "Betriebszeit FHEM"; #$rMap->{"fhemuptime_text"} = "Betriebszeit FHEM"; #$rMap->{"idletime"} = "Leerlaufzeit"; #$rMap->{"idletime_text"} = "Leerlaufzeit"; #$rMap->{"loadavg"} = "Durchschnittliche Auslastung"; #$rMap->{"ram"} = "RAM"; #$rMap->{"swap"} = "Swap"; #$rMap->{"uptime"} = "Betriebszeit"; #$rMap->{"uptime_text"} = "Betriebszeit"; $rMap->{"fhemuptime"} = "System up time"; $rMap->{"fhemuptime_text"} = "FHEM up time"; $rMap->{"idletime"} = "Idle time"; $rMap->{"idletime_text"} = "Idle time"; $rMap->{"loadavg"} = "Load average"; $rMap->{"loadavg_1"} = "Load average 1"; $rMap->{"loadavg_5"} = "Load average 5"; $rMap->{"loadavg_15"} = "Load average 15"; $rMap->{"ram"} = "RAM"; $rMap->{"ram_used_stat"} = "RAM used stat"; $rMap->{"ram_total"} = "RAM total"; $rMap->{"ram_used"} = "RAM used"; $rMap->{"ram_free"} = "RAM free"; $rMap->{"ram_free_percent"}= "RAM free %"; $rMap->{"swap"} = "swap"; $rMap->{"swap_used_stat"} = "swap used stat"; $rMap->{"swap_total"} = "swap total"; $rMap->{"swap_used"} = "swap used"; $rMap->{"swap_free"} = "swap free"; $rMap->{"swap_used_percent"}= "swap used %"; $rMap->{"uptime"} = "System up time"; $rMap->{"uptime_text"} = "System up time"; $rMap->{+STARTTIME_TEXT} = "System start time"; $rMap->{+STARTTIME} = "System start time"; $rMap->{+FHEMSTARTTIME} = "Fhem start time"; $rMap->{+FHEMSTARTTIME_TEXT} = "Fhem start time"; # Werte fuer GesamtCPU $rMap->{"stat_cpu"} = "CPU statistics"; $rMap->{"stat_cpu_diff"} = "CPU statistics (diff)"; $rMap->{"stat_cpu_percent"} = "CPU statistics (diff, percent)"; $rMap->{"stat_cpu_text"} = "CPU statistics (text)"; $rMap->{"cpu_idle_stat"} = "CPU min/max/avg (idle)"; $rMap->{"stat_cpu_user_percent"} = "CPU statistics user %"; $rMap->{"stat_cpu_nice_percent"} = "CPU statistics nice %"; $rMap->{"stat_cpu_sys_percent"} = "CPU statistics sys %"; $rMap->{"stat_cpu_idle_percent"} = "CPU statistics idle %"; $rMap->{"stat_cpu_io_percent"} = "CPU statistics io %"; $rMap->{"stat_cpu_irq_percent"} = "CPU statistics irq %"; $rMap->{"stat_cpu_sirq_percent"} = "CPU statistics sirq %"; # CPU 0-7 (sollte reichen) for my $i (0..7) { $rMap->{"stat_cpu".$i} = "CPU".$i." statistics"; $rMap->{"stat_cpu".$i."_diff"} = "CPU".$i." statistics (diff)"; $rMap->{"stat_cpu".$i."_percent"} = "CPU".$i." statistics (diff, percent)"; $rMap->{"stat_cpu".$i."_text"} = "CPU".$i." statistics (text)"; $rMap->{"cpu".$i."_idle_stat"} = "CPU".$i." min/max/avg (idle)"; } # Filesystems [:[:]] my $filesystems = AttrVal($name, "filesystems", undef); if(defined $filesystems) { my @filesystem_list = split(/,\s*/, trim($filesystems)); foreach (@filesystem_list) { my($fName, $fDef, $nComment) = split(/:/, $_); my $fPt; if(defined $nComment) { $fPt = $nComment; } else { if(defined $fDef) { # Benannte $fPt = "Filesystem ".$fDef; } else { # Unbenannte $fPt = "Mount point ".$fName; } } $rMap->{$fName} = $fPt; $rMap->{$fName."_used"} = $fPt." (used)"; $rMap->{$fName."_used_percent"} = $fPt." (used %)"; $rMap->{$fName."_free"} = $fPt." (free)"; } } else { $rMap->{"root"} = "Filesystem /"; } # Networkadapters: [:[:]] my $networkadapters = AttrVal($name, "network-interfaces", undef); if(defined $networkadapters) { my @networkadapters_list = split(/,\s*/, trim($networkadapters)); foreach (@networkadapters_list) { my($nName, $nDef, $nComment) = split(/:/, $_); my $nPt; if(defined $nComment) { $nPt = $nComment; } else { if(defined $nDef) { # Benannte $nPt = "Network ".$nDef; } else { # Unbenannte $nPt = "Network adapter ".$nName; } } $rMap->{$nName} = $nPt; $rMap->{$nName."_diff"} = $nPt." (diff)"; $rMap->{$nName."_speed"} = $nPt." (speed)"; $rMap->{$nName."_rx"} = $nPt." (RX)"; $rMap->{$nName."_tx"} = $nPt." (TX)"; $rMap->{$nName."_ip"} = $nPt." (IP)"; $rMap->{$nName."_ip6"} = $nPt." (IP6)"; } } else { # Default Networkadapters # Wenn nichts definiert, werden Default-Werte verwendet if(SYSMON_isFB($hash)) { my $nName = "ath0"; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; $nName = "ath1"; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; $nName = "cpmac0"; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; $nName = "dsl"; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; $nName = ETH0; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; $nName = "guest"; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; $nName = "hotspot"; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; $nName = "lan"; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; $nName = "vdsl"; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; } else { my $nName = ETH0; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; $nName = WLAN0; $rMap->{$nName} = "Network adapter ".$nName; $rMap->{$nName."_diff"} = "Network adapter ".$nName." (diff)"; $rMap->{$nName."_speed"} = "Network adapter ".$nName." (speed)"; $rMap->{$nName."_rx"} = "Network adapter ".$nName." (RX)"; $rMap->{$nName."_tx"} = "Network adapter ".$nName." (TX)"; $rMap->{$nName."_ip"} = "Network adapter ".$nName." (IP)"; $rMap->{$nName."_ip6"} = "Network adapter ".$nName." (IP6)"; } } if(SYSMON_isFB($hash)) { # FB WLAN state $rMap->{+FB_WLAN_STATE} = "WLAN State"; $rMap->{+FB_WLAN_GUEST_STATE} = "WLAN Guest State"; $rMap->{+FB_INET_IP} = "Internet IP"; $rMap->{+FB_INET_STATE} = "Internet connection state"; $rMap->{+FB_N_TIME_CTRL} = "night time control"; $rMap->{+FB_NUM_NEW_MESSAGES} = "new messages"; $rMap->{+FB_FW_VERSION} = "firmware info"; $rMap->{+FB_DECT_TEMP} = "DECT temperatur"; $rMap->{+FB_DSL_RATE} = "DSL rate", $rMap->{+FB_DSL_SYNCTIME} = "DSL synctime"; $rMap->{+FB_DSL_FEC_15} = "DSL recoverable errors per 15 minutes"; # forward error correction $rMap->{+FB_DSL_CRC_15} = "DSL unrecoverable errors per 15 minutes"; # cyclic redundancy check } # User defined my $userdefined = AttrVal($name, "user-defined", undef); if(defined $userdefined) { my @userdefined_list = split(/,\s*/, trim($userdefined)); foreach (@userdefined_list) { # ::: my($uName, $uInterval, $uComment, $uCmd) = split(/:/, $_); if(defined $uComment) { # Nur gueltige $rMap->{$uName} = $uComment; } } } # User defined functions my $userfn = AttrVal($name, "user-fn", undef); if(defined $userfn) { my @userfn_list = split(/,\s*/, trim($userfn)); foreach (@userfn_list) { # :::... my($fnName, $uInterval, @readings) = split(/:/, $_); foreach my $rName (@readings) { $rMap->{$rName} = "user defined: $fnName"; } } } # TEST: TODO $rMap->{"io_sda_raw"} = "TEST"; $rMap->{"io_sda_diff"} = "TEST"; $rMap->{"io_sda"} = "TEST"; $hash->{helper}{cur_readings_map} = $rMap; return $rMap; } sub SYSMON_getObsoleteReadingsMap($) { my ($hash) = @_; my $name = $hash->{NAME}; my $rMap; #return $rMap; # TODO TEST if(!defined($hash->{helper}{cur_readings_map})) { SYSMON_updateCurrentReadingsMap($hash); } # alle READINGS durchgehen my @cKeys=keys (%{$defs{$name}{READINGS}}); foreach my $aName (@cKeys) { if(defined ($aName)) { # alles hinzufuegen, was nicht in der Aktuellen Liste ist if(!defined($hash->{helper}{cur_readings_map}->{$aName})) { #Log 3, "SYSMON>>>>>>>>>>>>>>>>> SYSMON_getObsoleteReadingsMap >>> $aName"; $rMap->{$aName} = 1; } } } return $rMap; } sub SYSMON_Undefine($$) { my ($hash, $arg) = @_; SYSMON_Log($hash, 5, "$arg"); RemoveInternalTimer($hash); BlockingKill( $hash->{helper}{READOUT_RUNNING_PID} ) if exists $hash->{helper}{READOUT_RUNNING_PID}; return undef; } sub SYSMON_Get($@) { my ($hash, @a) = @_; my $name = $a[0]; if(@a < 2) { SYSMON_Log($hash, 3, "@a: get needs at least one parameter"); return "$name: get needs at least one parameter"; } my $cmd= $a[1]; SYSMON_Log($hash, 5, "@a"); if($cmd eq "update") { #$hash->{LOCAL} = 1; SYSMON_Update($hash, 1); #delete $hash->{LOCAL}; return undef; } if($cmd eq "list") { my $map = SYSMON_obtainParameters($hash, 1); my $ret = ""; foreach my $name (keys %{$map}) { my $value = $map->{$name}; $ret = "$ret\n".sprintf("%-20s %s", $name, $value); } my $msg = $hash->{helper}{error_msg}; if($msg) { # Problem mit der Verbindung return $msg; } return $ret; } if($cmd eq "version") { return $VERSION; } if($cmd eq "interval_base") { return $hash->{INTERVAL_BASE}; } if($cmd eq "interval_multipliers") { return $hash->{INTERVAL_MULTIPLIERS}; } if($cmd eq "list_lan_devices") { my $ret=''; my $map = SYSMON_getFBLanDeviceList($hash); if(defined($map)) { foreach my $dname (sort keys %{$map}) { my $dev_ip = $map->{$dname}{ip}; $dev_ip='' unless defined $dev_ip; my $dev_mac = $map->{$dname}{mac}; my $dev_active = $map->{$dname}{active}; my $dev_active_txt = $dev_active?'true':'false'; #$ret.="\n"."$dname : active: $dev_active_txt, IP: $dev_ip, MAC: $dev_mac"; $ret = "$ret\n".sprintf("%-25s : active: %-5s IP: %-16s MAC: %-17s", $dname, $dev_active_txt, $dev_ip, $dev_mac); } } return $ret; } my $sfb=''; if(SYSMON_isFB($hash)) { $sfb=' list_lan_devices:noArg'; } return "Unknown argument $cmd, choose one of list:noArg update:noArg interval_base:noArg interval_multipliers:noArg version:noArg".$sfb; } sub SYSMON_Set($@) { my ($hash, @a) = @_; my $name = $a[0]; if(@a < 2) { SYSMON_Log($hash, 3, "@a: set needs at least one parameter"); return "$name: set needs at least one parameter"; } my $cmd= $a[1]; SYSMON_Log($hash, 5, "@a"); if($cmd eq "interval_multipliers") { if(@a < 3) { SYSMON_Log($hash, 3, "$name: not enought parameters"); return "$name: not enought parameters"; } my @na = @a[2..scalar(@a)-1]; SYSMON_setInterval($hash, @na); return $cmd ." set to ".($hash->{INTERVAL_MULTIPLIERS}); } if($cmd eq "clean") { # Nicht mehr benoetigte Readings loeschen my $omap = SYSMON_getObsoleteReadingsMap($hash); foreach my $aName (keys %{$omap}) { delete $defs{$name}{READINGS}{$aName}; } return; } if($cmd eq "clear") { my $subcmd = my $cmd= $a[2]; if(defined $subcmd) { delete $defs{$name}{READINGS}{$subcmd}; return; } return "missing parameter. use clear "; } if ( lc $cmd eq 'password') { my $subcmd = $a[2]; if(defined $subcmd) { my $ret = SYSMON_storePassword ($hash, $subcmd); if(!defined($hash->{helper}{error_msg})) { SYSMON_Update($hash, 1); } return $ret; } } # TEST if($cmd eq "reset") { delete $defs{$name}->{helper}; return 'ok'; } return "Unknown argument $cmd, choose one of password interval_multipliers clean:noArg clear"; } sub SYSMON_Attr($$$) { my ($cmd, $name, $attrName, $attrVal) = @_; my $hash = $main::defs{$name}; SYSMON_Log($hash, 5, "SYSMON Attr: $cmd $name ".$attrName?$attrName:''." $attrVal"); $attrVal= "" unless defined($attrVal); my $orig = AttrVal($name, $attrName, ""); if( $orig ne $attrVal ) { if( $cmd eq "set" ) {# set, del if($attrName eq "exclude") { my @elist = split(/,\s*/, trim($attrVal)); my %ehash = map { $_ => 1 } @elist; $hash->{helper}->{excludes}=\%ehash; } if($attrName eq "disable") { RemoveInternalTimer($hash); if($attrVal ne "1") { InternalTimer(gettimeofday()+$hash->{INTERVAL_BASE}, "SYSMON_Update", $hash, 0); $hash->{STATE} = "Active"; } else { $hash->{STATE} = "Inactive"; } #$hash->{LOCAL} = 1; #SYSMON_Update($hash); #delete $hash->{LOCAL}; } $attr{$name}{$attrName} = $attrVal; SYSMON_updateCurrentReadingsMap($hash); #return $attrName ." set to ". $attrVal; return undef; } elsif( $cmd eq "del" ) { if($attrName eq "exclude") { $hash->{helper}->{excludes}=undef; } } } return; } #my $u_first_mark = undef; sub SYSMON_Update($;$) { my ($hash, $refresh_all) = @_; $refresh_all="0" unless defined $refresh_all; #SYSMON_Log($hash, 5, "refresh_all: ".$refresh_all); my $name = $hash->{NAME}; if(!$hash->{LOCAL}) { RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$hash->{INTERVAL_BASE}, "SYSMON_Update", $hash, 1); } return unless($init_done); if( AttrVal($name, "disable", "") eq "1" ) { #SYSMON_Log($hash, 5, "disabled"); #$hash->{STATE} = "Inactive"; } else { # Beim ersten mal alles aktualisieren! if(!$hash->{helper}{u_first_mark}) { $refresh_all = 1; } SYSMON_obtainLocalCPUFreq($hash); my $map; if(!AttrVal($name, "nonblocking", 1)) { # direkt call # Parameter holen $map = SYSMON_obtainParameters($hash, $refresh_all); # Mark setzen if(!$hash->{helper}{u_first_mark}) { $hash->{helper}{u_first_mark} = 1; } SYSMON_updateReadings($hash,$map); #$hash->{STATE} = "Active"; } else { # blocking call if ( exists( $hash->{helper}{READOUT_RUNNING_PID} ) ) { SYSMON_Log($hash, 5, "blockingCall: Old readout process still running. Killing old process ".$hash->{helper}{READOUT_RUNNING_PID}); BlockingKill( $hash->{helper}{READOUT_RUNNING_PID} ); delete($hash->{helper}{READOUT_RUNNING_PID}); } $hash->{helper}{READOUT_RUNNING_PID} = BlockingCall("SYSMON_blockingCall", $name."|".$refresh_all, "SYSMON_blockingFinish", 55, "SYSMON_blockingAbort", $hash); } } } sub SYSMON_obtainLocalCPUFreq($) { my ($hash) = @_; my $map; #-------------------------------------------------------------------------- my $base=$DEFAULT_INTERVAL_BASE; my $im = "1 1 1 10"; # Wenn wesentliche Parameter nicht definiert sind, soll aktualisierung immer vorgenommen werden if((defined $hash->{INTERVAL_BASE})) { $base = $hash->{INTERVAL_BASE}; } if((defined $hash->{INTERVAL_MULTIPLIERS})) { $im = $hash->{INTERVAL_MULTIPLIERS}; } my $ref = int(time()/$base); my ($m1, $m2, $m3, $m4) = split(/\s+/, $im); if($m1 gt 0) { # Nur wenn > 0 # M1: cpu_freq, cpu_temp, cpu_temp_avg, loadavg, procstat, iostat if(($ref % $m1) eq 0) { # Sonderlocke: CPUFreq my $mode = $hash->{MODE}; if ($mode eq 'local') { foreach my $li (0..7) { if(SYSMON_isCPUXFreq($hash, $li)) { $map = SYSMON_getCPUFreqLocal($hash, $map, $li); } } } } } #-------------------------------------------------------------------------- SYSMON_updateReadings($hash,$map); } sub SYSMON_blockingCall($) { my ($tparam) = @_; my ($name, $refresh_all) = split(/\|/,$tparam); my $hash = $main::defs{$name}; SYSMON_Log($hash, 5, "$name, ".($refresh_all?$refresh_all:'')); my $map = SYSMON_obtainParameters($hash, $refresh_all); # Device-Name mitnehmen my $ret = "name|".$name; my $msg = $hash->{helper}{error_msg}; if($msg) { # Problem mit der Verbindung return $ret."|error|".$msg; } # to String foreach my $aName (keys %{$map}) { my $value = $map->{$aName}; # Nur wenn ein gueltiges Value vorliegt if(defined $value) { # Zeichen maskieren $value=~s/#/§²§/g; $value=~s/\|/§³§/g; $ret.="|".$aName."|".$value; } } return $ret; } sub SYSMON_test() { #foreach my $d (sort keys %defs) { # my $h = $defs{$d}; # if(defined ($h->{TYPE})) {} else {return $d."-".Dumper($h);} #} my $map; my $name="TESTNAME"; $map->{test1}="val1"; $map->{test2}="val2"; $map->{test3}="val3"; #return Dumper($map); my $ret = "name|".$name; # to ret String foreach my $aName (keys %{$map}) { my $value = $map->{$aName}; # Nur wenn ein gueltiges Value vorliegt if(defined $value) { $value=~s/#/§²§/g; $ret.="|".$aName."|".$value; } } my @ta = split(/\|/,$ret); #return Dumper(@ta); my %map2 = @ta; return Dumper(\%map2); return $ret; } sub SYSMON_blockingAbort($) { my ($hash) = @_; delete($hash->{helper}{READOUT_RUNNING_PID}); SYSMON_Log($hash, 5, ""); $hash->{STATE} = "Error: Blocking call aborted (timeout)"; } sub SYSMON_blockingFinish($) { my ($map_str) = @_; my $map; # to map my @ta = split(/\|/,$map_str); my %tm = @ta; $map = \%tm; my $name=$map->{name}; delete $map->{name}; my $hash = $main::defs{$name}; delete($hash->{helper}{READOUT_RUNNING_PID}); SYSMON_Log($hash, 5, $map_str); # Mark setzen if(!$hash->{helper}{u_first_mark}) { $hash->{helper}{u_first_mark} = 1; } my $msg = $map->{error}; if($msg) { # Im Fehlerfall State ebtsprechend setzen und nichts aktualisieren. $hash->{STATE} = "Error: ".$msg; return; } SYSMON_updateReadings($hash,$map); #$hash->{STATE} = "Active"; } sub SYSMON_updateReadings($$) { my ($hash,$map) = @_; SYSMON_Log($hash, 5, ""); my $name = $hash->{NAME}; readingsBeginUpdate($hash); # Wenn UserFn benutzt wird, werden die erstellten Eintraege erfasst und die entsprechenden Readings nicht erhalten my $h_keys; my $uFnReadings = $map->{"xuser_fnr"}; my @a_keys; if(defined($uFnReadings)) { delete $map->{"xuser_fnr"}; @a_keys = split(/,\s*/, trim($uFnReadings)); #$h_keys = map { $_ => "1" } @a_keys; } foreach my $aName (keys %{$map}) { my $value = $map->{$aName}; # Nur aktualisieren, wenn ein gueltiges Value vorliegt if(defined $value) { # Maskierte Zeichen zuruechersetzen $value=~s/§²§/#/g; $value=~s/§³§/\|/g; readingsBulkUpdate($hash,$aName,$value); } } # Nicht mehr benoetigte Readings loeschen #my $omap = SYSMON_getObsoleteReadingsMap($hash); # # UserFn Keys entfernen #foreach my $aName (@a_keys) { # delete($omap->{$aName}); #} #foreach my $aName (keys %{$omap}) { # # SYSMON_Log($hash, 5, ">>>>>>>>>>>>>>>>>>>> ".$aName."->".Dumper($defs{$name}{READINGS}{$aName})); # delete $defs{$name}{READINGS}{$aName}; #} readingsEndUpdate($hash,defined($hash->{LOCAL}) ? 0 : 1); } sub SYSMON_obtainParameters($$) { my ($hash, $refresh_all) = @_; my $name = $hash->{NAME}; # --- #TODO: SSH my $msg = undef; my $openedTelnet = 0; my $telnet = $hash->{".telnet"}; #$telnet = undef; my $mode = $hash->{MODE}; # Wenn remote: open connection if ($mode eq 'telnet') { unless (defined $telnet) { SYSMON_Log($hash, 5, "$name: Open shared telnet connection"); $msg = SYSMON_Open_Connection($hash); $hash->{helper}{error_msg}=$msg; if (!$msg) { $openedTelnet = 1; $hash->{helper}{error_msg}=undef; } } } # --- my $map; if (!$msg) { $map = SYSMON_obtainParameters_intern($hash, $refresh_all); } # --- # Wenn remote: close connection if ($mode eq 'telnet') { if($openedTelnet) { SYSMON_Log($hash, 5, "$name: Close shared telnet connection"); SYSMON_Close_Connection( $hash ); } } # --- return $map; } # Schattenmap mit den zuletzt gesammelten Werten (merged) #my %shadow_map; sub SYSMON_obtainParameters_intern($$) { my ($hash, $refresh_all) = @_; my $name = $hash->{NAME}; my $map; my $base=$DEFAULT_INTERVAL_BASE; my $im = "1 1 1 10"; # Wenn wesentliche Parameter nicht definiert sind, soll aktualisierung immer vorgenommen werden if((defined $hash->{INTERVAL_BASE})) { $base = $hash->{INTERVAL_BASE}; } if((defined $hash->{INTERVAL_MULTIPLIERS})) { $im = $hash->{INTERVAL_MULTIPLIERS}; } my $ref = int(time()/$base); my ($m1, $m2, $m3, $m4) = split(/\s+/, $im); my $mode = $hash->{MODE};#AttrVal( $name, 'mode', 'local'); # Einmaliges if(!$hash->{helper}{u_first_mark}) { # nur lokal abfragen (macht remote keinen Sinn) if ($mode eq 'local') { # Perl version $map->{+PERL_VERSION} = "$^V"; } if(SYSMON_isProcFS($hash)) { $map = SYSMON_getCPUInfo($hash, $map); } if(SYSMON_isFB($hash)) { $map = SYSMON_FBVersionInfo($hash, $map); } } # immer aktualisieren: uptime, uptime_text, fhemuptime, fhemuptime_text, idletime, idletime_text if(SYSMON_isProcFS($hash)) { $map = SYSMON_getUptime($hash, $map); } else { $map = SYSMON_getUptime2($hash, $map); } # nur lokal abfragen if ($mode eq 'local') { $map = SYSMON_getFHEMUptime($hash, $map); } if($m1 gt 0) { # Nur wenn > 0 # M1: cpu_freq, cpu_temp, cpu_temp_avg, loadavg, procstat, iostat if($refresh_all || ($ref % $m1) eq 0) { $map = SYSMON_getCPUCoreNum($hash, $map); #Log 3, "SYSMON -----------> DEBUG: read CPU-Temp"; if(SYSMON_isCPUTempRPi($hash)) { # Rasp $map = SYSMON_getCPUTemp_RPi($hash, $map); } if (SYSMON_isCPUTempBBB($hash)) { $map = SYSMON_getCPUTemp_BBB($hash, $map); } foreach my $li (0..7) { if(SYSMON_isCPUTemp_X($hash, $li)) { $map = SYSMON_getCPUTemp_X($hash, $map, $li); } } if (SYSMON_isCPUTempFB($hash)) { $map = SYSMON_getCPUTemp_FB($hash, $map); } #if(SYSMON_isCPUFreqRPiBBB($hash)) { # $map = SYSMON_getCPUFreq($hash, $map, 0); #} foreach my $li (0..7) { if(SYSMON_isCPUXFreq($hash, $li)) { $map = SYSMON_getCPUFreq($hash, $map, $li); } } if(SYSMON_isProcFS($hash)) { $map = SYSMON_getLoadAvg($hash, $map); $map = SYSMON_getCPUProcStat($hash, $map); } else { #TODO: Ohne ProcFS } #$map = SYSMON_getDiskStat($hash, $map); # Power info (cubietruck) if(SYSMON_isSysPowerAc($hash)) { $map = SYSMON_PowerAcInfo($hash, $map); } if(SYSMON_isSysPowerUsb($hash)) { $map = SYSMON_PowerUsbInfo($hash, $map); } if(SYSMON_isSysPowerBat($hash)) { $map = SYSMON_PowerBatInfo($hash, $map); } } } if($m2 gt 0) { # Nur wenn > 0 # M2: ram, swap if($refresh_all || ($ref % $m2) eq 0) { if(SYSMON_isOSX()){ $map = SYSMON_getRamAndSwapOSX($hash, $map); } else { $map = SYSMON_getRamAndSwap($hash, $map); } } } if($m3 gt 0) { # Nur wenn > 0 # M3: eth0, eth0_diff, wlan0, wlan0_diff, wlan_on (FritzBox) my $update_ns = ($refresh_all || ($ref % $m3) eq 0); #if($refresh_all || ($ref % $m3) eq 0) { my $networks = AttrVal($name, "network-interfaces", undef); if($update_ns) { if(defined $networks) { my @networks_list = split(/,\s*/, trim($networks)); foreach (@networks_list) { $map = SYSMON_getNetworkInfo($hash, $map, $_); } } else { # Wenn nichts definiert, werden Default-Werte verwendet #Log 3, "SYSMON>>>>>>>>>>>>>>>>>>>>>>>>> NETWORK"; if(SYSMON_isFB($hash)) { $map = SYSMON_getNetworkInfo($hash, $map, "ath0"); $map = SYSMON_getNetworkInfo($hash, $map, "ath1"); $map = SYSMON_getNetworkInfo($hash, $map, "cpmac0"); $map = SYSMON_getNetworkInfo($hash, $map, "dsl"); $map = SYSMON_getNetworkInfo($hash, $map, "eth0"); $map = SYSMON_getNetworkInfo($hash, $map, "guest"); $map = SYSMON_getNetworkInfo($hash, $map, "hotspot"); $map = SYSMON_getNetworkInfo($hash, $map, "lan"); $map = SYSMON_getNetworkInfo($hash, $map, "vdsl"); } else { #Log 3, "SYSMON>>>>>>>>>>>>>>>>>>>>>>>>> ".ETH0; $map = SYSMON_getNetworkInfo($hash, $map, ETH0); #Log 3, "SYSMON>>>>>>>>>>>>>>>>>>>>>>>>> ".$map->{+ETH0}; #Log 3, "SYSMON>>>>>>>>>>>>>>>>>>>>>>>>> ".WLAN0; $map = SYSMON_getNetworkInfo($hash, $map, WLAN0); #Log 3, "SYSMON>>>>>>>>>>>>>>>>>>>>>>>>> ".$map->{+WLAN0}; } } if(SYSMON_isFB($hash)) { $map = SYSMON_getFBWLANState($hash, $map); $map = SYSMON_getFBWLANGuestState($hash, $map); $map = SYSMON_getFBInetIP($hash, $map); $map = SYSMON_getFBInetConnectionState($hash, $map); $map = SYSMON_getFBNightTimeControl($hash, $map); $map = SYSMON_getFBNumNewMessages($hash, $map); $map = SYSMON_getFBDECTTemp($hash, $map); #DSL-Downstream und DSL-Upstream abfragen $map = SYSMON_getFBStreamRate($hash, $map); #Sync-Zeit mit Vermittlungsstelle abfragen $map = SYSMON_getFBSyncTime($hash, $map); #Uebertragungsfehler abfragen (nicht behebbar und behebbar) $map = SYSMON_getFBCRCFEC($hash, $map); } } } if($m4 gt 0) { # Nur wenn > 0 # M4: Filesystem-Informationen my $update_fs = ($refresh_all || ($ref % $m4) eq 0); my $filesystems = AttrVal($name, "filesystems", undef); if($update_fs) { if(defined $filesystems) { my @filesystem_list = split(/,\s*/, trim($filesystems)); foreach (@filesystem_list) { $map = SYSMON_getFileSystemInfo($hash, $map, $_); } } else { $map = SYSMON_getFileSystemInfo($hash, $map, "root:/"); } } else { # Workaround: Damit die Readings zw. den Update-Punkten nicht geloescht werden, werden die Schluessel leer angelegt # Wenn noch keine Update notwendig, dan einfach alte Schluessel (mit undef als Wert) angeben, # damit werden die Readings in der Update-Methode nicht geloescht. # Die ggf. notwendige Loeschung findet nur bei tatsaechlichen Update statt. my @cKeys=keys (%{$defs{$name}{READINGS}}); foreach my $aName (@cKeys) { #if(defined ($aName) && (index($aName, FS_PREFIX) == 0 || index($aName, FS_PREFIX_N) == 0)) { if(defined ($aName) && (index($aName, FS_PREFIX) == 0 )) { $map->{$aName} = undef; } } } } #Log 3, "SYSMON >>> USER_DEFINED >>>>>>>>>>>>>>> START"; my $userdefined = AttrVal($name, "user-defined", undef); if(defined $userdefined) { my @userdefined_list = split(/,\s*/, trim($userdefined)); foreach (@userdefined_list) { # ::: my $ud = $_; my($uName, $uInterval, $uComment, $uCmd) = split(/:/, $ud); SYSMON_Log($hash, 5, "User-Defined Reading: [$uName][$uInterval][$uComment][$uCmd]"); if(defined $uCmd) { # Also, wenn alle Parameter vorhanden my $iInt = int($uInterval); if($iInt>0) { my $update_ud = ($refresh_all || ($ref % $iInt) eq 0); if($update_ud) { $map = SYSMON_getUserDefined($hash, $map, $uName, $uCmd); } else { SYSMON_Log($hash, 5, "User-Defined Reading: [$uName][$uInterval][$uComment][$uCmd] out of refresh interval"); } } } } } #Log 3, "SYSMON >>> USER_DEFINED FUNCTIONS >>>>>>>>>>>>>>> START"; my $userfn = AttrVal($name, "user-fn", undef); if(defined $userfn) { my @userfn_list = split(/,\s+/, trim($userfn)); foreach my $ud (@userfn_list) { # :::..., [:...] my($fnName, $uInterval, @readings) = split(/:/, $ud); SYSMON_Log($hash, 5, "User-Defined Fn: [$fnName][$uInterval]"); if(defined $uInterval) { my $iInt = int($uInterval); if($iInt>0) { my $update_ud = ($refresh_all || ($ref % $iInt) eq 0); if($update_ud) { $map = SYSMON_getUserDefinedFn($hash, $map, $fnName, @readings); } else { SYSMON_Log($hash, 5, "User-Defined Fn: [$fnName][$uInterval] out of refresh interval"); } } } } } # User Functions2 my $uMap; $userfn = AttrVal($name, "user-fn2", undef); #TEST$userfn=undef; if(defined $userfn) { my @userfn_list = split(/,\s*/, trim($userfn)); foreach (@userfn_list) { my $ufn = $_; SYSMON_Log($hash, 5, "User-Function Reading: [$ufn]"); if(defined $ufn) { no strict "refs"; $uMap = &{$ufn}($hash, $uMap); use strict "refs"; } } } # Werte umverpacken, KeyNamen sichern my $uNames=''; if(defined($uMap)) { foreach my $uName (keys %{$uMap}) { $uNames.=','.$uName; $map->{$uName}=$uMap->{$uName}; } # Erste Komma entfernen $uNames=substr($uNames,1); $map->{"xuser_fnr"}=$uNames; } #TEST #my $rt = "#"; #$rt=~s/#/[]/g; #$map->{SYS_TEST}=$rt; if(defined($map)) { # Aktuelle Werte in ShattenHash mergen my %hashT = %{$map}; #@shadow_map{ keys %hashT } = values %hashT; my %shadow_map; @shadow_map{ keys %hashT } = values %hashT; $hash->{helper}{shadow_map} = \%shadow_map; } return $map; } # For test purpose only sub SYSMON_TestUserFn($$) { my ($hash, $map) = @_; $map->{"my_test_reading"}="my test"; #$map->{"my"}="my"; return $map; } #------------------------------------------------------------------------------ # Liefert gesammelte Werte ( = Readings) # Parameter: array der gewuenschten keys (Readings names) # Beispiele: # {(SYSMON_getValues("sysmon"))->{'cpu_temp'}} # {(SYSMON_getValues("sysmon",("cpu_freq","cpu_temp")))->{"cpu_temp"}} # {join(" ", values (SYSMON_getValues("sysmon")))} # {join(" ", values (SYSMON_getValues("sysmon",("cpu_freq","cpu_temp"))))} #------------------------------------------------------------------------------ sub SYSMON_getValues($;@) { my ($name,@filter_keys) = @_; my $hash = $main::defs{$name}; my %shadow_map = %{$hash->{helper}{shadow_map}}; if(scalar(@filter_keys)>0) { my %clean_hash; #@clean_hash{ @filter_keys } = @shadow_map{ @filter_keys }; @clean_hash{ @filter_keys } = @shadow_map{ @filter_keys }; return \%clean_hash; } # alles liefern return \%shadow_map; } sub SYSMON_getComputeStat($$$$) { my ($hash, $map, $val, $name) = @_; if (defined($val)) { my $t = ReadingsVal($hash->{NAME},$name,"$val $val $val"); my($min, $max, $avg) = split(/ /,$t); $min = $val if $min>$val; $max = $val if $max<$val; $avg = (3*$avg + $val)/4; $t = sprintf( "%.2f %.2f %.2f", $min, $max, $avg ); $map->{$name} = $t; #SYSMON_Log($hash, 3, ">>>>>>>>>>>>>>>>> ".$name." => $t"); } return $map; } #------------------------------------------------------------------------------ # Liest Benutzerdefinierte Eintraege #------------------------------------------------------------------------------ sub SYSMON_getUserDefined($$$$) { my ($hash, $map, $uName, $uCmd) = @_; SYSMON_Log($hash, 5, "Name=[$uName] Cmd=[$uCmd]"); if($hash->{helper}->{excludes}{'user-defined'}) {return $map;} my @out_arr = SYSMON_execute($hash, $uCmd); my $out_str = ""; foreach my $k (@out_arr) { if(defined($k)) { chomp($k); $out_str.=$k." "; } } #my $out_str = join(" ",@out_arr); ##my $out_str = SYSMON_execute($hash, $uCmd); ##chomp $out_str; #$out_str=~s/\n/ /g; #$out_str=~s/\r/ /g; $map->{$uName} = $out_str; SYSMON_Log($hash, 5, "User-Defined Result: $uName='$out_str'"); return $map; } sub SYSMON_getUserDefinedFn($$$@) { my($hash, $map, $fnName, @readings) = @_; #SYSMON_Log($hash, 3, ">>>>>>>>>>>>>>>>>>>>> exclude: ".Dumper($hash->{helper}->{excludes})); if($hash->{helper}->{excludes}{'user-defined'}) {return $map;} SYSMON_Log($hash, 5, "call User-Function: [$fnName]"); if(defined $fnName) { no strict "refs"; my @rarr; if($fnName=~/^{/) { my $HASH = $hash; my $NAME = $hash->{NAME}; @rarr = eval($fnName); } else { @rarr = &{$fnName}($hash); } use strict "refs"; SYSMON_Log($hash, 5, "result User-Function [$fnName]: ".Dumper(@rarr)); my $cnt1 = scalar(@readings); my $cnt2 = scalar(@rarr); my $cnt = min($cnt1,$cnt2); if($cnt1!=$cnt2) { # zu wenig readings geliefert ($cnt1>$cnt2) oder zu viel SYSMON_Log($hash, 3, "User-Function [$fnName]: expected readings: [$cnt1], provided [$cnt2]"); } #SYSMON_Log($hash, 5, ">>>> User-Function [$fnName]: $cnt1 / $cnt2: $rarr[0]"); for (my $i=0;$i<$cnt;$i++) { if(defined($rarr[$i])) { my $val = trim($rarr[$i]); #SYSMON_Log($hash, 5, ">>>> User-Function [$fnName]: put: '".$readings[$i]."' => '".$val."'"); $map->{$readings[$i]} = $val; #$map->{$readings[$i]}="Dead OWTHERM devices: none"; #SYSMON_Log($hash, 5, ">>>> User-Function [$fnName]: ok"); } } } return $map; } #my $sys_cpu_core_num = undef; sub SYSMON_getCPUCoreNum_intern($) { my ($hash) = @_; return $hash->{helper}{sys_cpu_core_num} if $hash->{helper}{sys_cpu_core_num}; # TODO: Umstellung auf # cat /sys/devices/system/cpu/present # cat /sys/devices/system/cpu/online # nur wenn verfuegbar if(SYSMON_isSysCpuNum($hash)) { my $str = SYSMON_execute($hash, "cat /sys/devices/system/cpu/kernel_max"); if(defined($str)) { if($str ne "") { if(int($str)!=0) { $hash->{helper}{sys_cpu_core_num} = int($str)+1; return $hash->{helper}{sys_cpu_core_num}; } } } } # Default / unbekannt $hash->{helper}{sys_cpu_core_num} = 1; return $hash->{helper}{sys_cpu_core_num}; } #------------------------------------------------------------------------------ # leifert Anzahl CPU Kerne #------------------------------------------------------------------------------ sub SYSMON_getCPUCoreNum($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'cpucount'}) {return $map;} my $cpuCoreCnt = SYSMON_getCPUCoreNum_intern($hash); $map->{+CPU_CORE_CNT}=$cpuCoreCnt; return $map; } #------------------------------------------------------------------------------ # leifert Zeit seit dem Systemstart #------------------------------------------------------------------------------ sub SYSMON_getUptime($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'uptime'}) {return $map;} #my $uptime_str = qx(cat /proc/uptime ); my $uptime_str = SYSMON_execute($hash, "cat /proc/uptime"); if(defined($uptime_str)) { my ($uptime, $idle) = split(/\s+/, trim($uptime_str)); unless (defined ($idle)) { # Hack/Sonderlocke: https://forum.fhem.de/index.php/topic,118067.msg1126192.html#msg1126192 my $dotpos = index($uptime,'.'); $idle = substr($uptime, $dotpos+3, length($uptime)-$dotpos+3); $uptime = substr($uptime, 0, $dotpos+3); } #postfux use idle from /proc/stat instead my $stat_str = SYSMON_execute($hash, "cat /proc/stat|grep 'cpu '"); my($tName, $neuCPUuser, $neuCPUnice, $neuCPUsystem, $neuCPUidle, $neuCPUiowait, $neuCPUirq, $neuCPUsoftirq) = split(/\s+/, trim($stat_str)); if(defined($neuCPUidle)){ $idle=$neuCPUidle/100; } #postfux if(defined($uptime) && int($uptime)!=0) { # Anzahl Cores beruecksichtigen my $core_num = SYSMON_getCPUCoreNum_intern($hash); my $idle_percent = $idle/($uptime*$core_num)*100; $idle = $idle/$core_num; $map->{+UPTIME}=sprintf("%d",$uptime); #$map->{+UPTIME_TEXT} = sprintf("%d days, %02d hours, %02d minutes, %02d seconds",SYSMON_decode_time_diff($uptime)); $map->{+UPTIME_TEXT} = sprintf("%d days, %02d hours, %02d minutes",SYSMON_decode_time_diff($uptime)); my $startTime = time()-$uptime; $map->{+STARTTIME} = sprintf("%d",$startTime); $map->{+STARTTIME_TEXT} = strftime("%d.%m.%Y %H:%M:%S", localtime($startTime)); $map->{+IDLETIME}=sprintf("%d %.2f %%",$idle, $idle_percent); $map->{+IDLETIME_TEXT} = sprintf("%d days, %02d hours, %02d minutes",SYSMON_decode_time_diff($idle)).sprintf(" (%.2f %%)",$idle_percent); #$map->{+IDLETIME_PERCENT} = sprintf ("%.2f %",$idle_percent); } } return $map; } #------------------------------------------------------------------------------ # leifert Zeit seit dem Systemstart. # Alternative Version fuer Systemen ohne procfs (z.B. MACOS) #------------------------------------------------------------------------------ sub SYSMON_getUptime2($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'uptime'}) {return $map;} #TODO my $uptime = SYSMON_execute($hash,"uptime"); if(defined($uptime)){ #SYSMON_Log($hash, 5, ">>>>>>>>>>>>>>>>>>>>>>".$uptime."<"); #$uptime = $1 if( $uptime && $uptime =~ m/[[:alpha:]]{2}\s+(((\d+)\D+,?\s+)?(\d+):(\d+))/ ); $uptime = $1 if( $uptime && $uptime =~ m/[[:alpha:]]{2}\s+(((\d+)\D+,?\s+)?(\d+):(\d+)).*load.*: (.*)/ ); $uptime = "0 days, $uptime" if( $uptime && !$2); my $days = $3?$3:0; my $hours = $4; my $minutes = $5; if(defined($days) && defined($hours) && defined($minutes)) { $uptime = $days * 24; $uptime += $hours; $uptime *= 60; $uptime += $minutes; $uptime *= 60; } else { $uptime = 0; } $map->{+UPTIME}=sprintf("%d",$uptime); $map->{+UPTIME_TEXT} = sprintf("%d days, %02d hours, %02d minutes",SYSMON_decode_time_diff($uptime)); my $startTime = time()-$uptime; $map->{+STARTTIME} = sprintf("%d",$startTime); $map->{+STARTTIME_TEXT} = strftime("%d.%m.%Y %H:%M:%S", localtime($startTime)); my $loadavg=$6; if(defined($loadavg)) { my ($la1, $la5, $la15, $prc, $lastpid) = split(/\s+/, trim($loadavg)); if(defined($la1) && defined($la5) && defined($la15)) { $la1 =~ s/,$//; $la5 =~ s/,$//; $la1 =~ s/,/./; $la5 =~ s/,/./; $la15 =~ s/,/./; $map->{+LOADAVG}="$la1 $la5 $la15"; } } } return $map; } #------------------------------------------------------------------------------ # leifert Zeit seit FHEM-Start #------------------------------------------------------------------------------ sub SYSMON_getFHEMUptime($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fhemuptime'}) {return $map;} #if(defined ($hash->{DEF_TIME})) { if(defined($fhem_started)) { #my $fhemuptime = time()-$hash->{DEF_TIME}; my $fhemuptime = time()-$fhem_started; $map->{+FHEMUPTIME} = sprintf("%d",$fhemuptime); $map->{+FHEMUPTIME_TEXT} = sprintf("%d days, %02d hours, %02d minutes",SYSMON_decode_time_diff($fhemuptime)); my $startTime = time()-$fhemuptime; $map->{+FHEMSTARTTIME} = sprintf("%d",$startTime); $map->{+FHEMSTARTTIME_TEXT} = strftime("%d.%m.%Y %H:%M:%S", localtime($startTime)); } return $map; } #------------------------------------------------------------------------------ # leifert CPU-Auslastung #------------------------------------------------------------------------------ sub SYSMON_getLoadAvg($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'loadavg'}) {return $map;} my $la_str = SYSMON_execute($hash, "cat /proc/loadavg"); if(defined($la_str)) { my ($la1, $la5, $la15, $prc, $lastpid) = split(/\s+/, trim($la_str)); if(defined($la1) && defined($la5) && defined($la15)) { $map->{+LOADAVG}="$la1 $la5 $la15"; #$map->{"load"}="$la1"; #$map->{"load5"}="$la5"; #$map->{"load15"}="$la15"; } } return $map; } #------------------------------------------------------------------------------ # liefert CPU Temperature (Raspberry Pi) #------------------------------------------------------------------------------ sub SYSMON_getCPUTemp_RPi($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'cputemp'}) {return $map;} my $val = SYSMON_execute($hash, "cat /sys/class/thermal/thermal_zone0/temp 2>&1"); $val = int($val); if($val>1000) { # Manche Systeme scheinen die Daten verschieden zu skalieren (z.B. utilite)... $val = $val/1000; } my $val_txt = sprintf("%.2f", $val); $map->{+CPU_TEMP}="$val_txt"; my $t_avg = sprintf( "%.1f", (3 * ReadingsVal($hash->{NAME},CPU_TEMP_AVG,$val_txt) + $val_txt ) / 4 ); $map->{+CPU_TEMP_AVG}="$t_avg"; $map = SYSMON_getComputeStat($hash, $map, $val_txt, CPU_TEMP."_stat"); return $map; } #------------------------------------------------------------------------------ # leifert CPU Temperature (BeagleBone Black) #------------------------------------------------------------------------------ sub SYSMON_getCPUTemp_BBB($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'cputemp'}) {return $map;} my $val = SYSMON_execute($hash, "cat /sys/class/hwmon/hwmon0/device/temp1_input 2>&1"); if(!looks_like_number($val)) {return $map;} $val = int($val); if ($val > 200) { $val = $val / 1000; } my $val_txt = sprintf("%.2f", $val); $map->{+CPU_TEMP}="$val_txt"; $map->{"cpu0_temp"}="$val_txt"; my $t_avg = sprintf( "%.1f", (3 * ReadingsVal($hash->{NAME},CPU_TEMP_AVG,$val_txt) + $val_txt ) / 4 ); $map->{+CPU_TEMP_AVG}=$t_avg; $t_avg = sprintf( "%.1f", (3 * ReadingsVal($hash->{NAME},"cpu0_temp_avg",$val_txt) + $val_txt ) / 4 ); $map->{"cpu0_temp_avg"}=$t_avg; $map = SYSMON_getComputeStat($hash, $map, $val_txt, CPU_TEMP."_stat"); return $map; } #------------------------------------------------------------------------------ # leifert CPU Temperature (mehrere Kerne eines ?) #------------------------------------------------------------------------------ sub SYSMON_getCPUTemp_X($$;$) { my ($hash, $map, $cpuNum) = @_; if($hash->{helper}->{excludes}{'cputemp'}) {return $map;} $cpuNum = 0 unless defined $cpuNum; my $val = SYSMON_execute($hash, "cat /sys/class/hwmon/hwmon0/device/hwmon/hwmon0/temp".($cpuNum+1)."_input 2>&1"); $val = int($val); my $val_txt = sprintf("%.2f", $val/1000); $map->{"cpu".$cpuNum."_temp"}="$val_txt"; my $t_avg = sprintf( "%.1f", (3 * ReadingsVal($hash->{NAME},"cpu".$cpuNum."_temp_avg",$val_txt) + $val_txt ) / 4 ); $map->{"cpu".$cpuNum."_temp_avg"}=$t_avg; $map = SYSMON_getComputeStat($hash, $map, $val_txt, "cpu".$cpuNum."_temp"."_stat"); return $map; } #------------------------------------------------------------------------------ # leifert CPU Temperature (FritzBox) #------------------------------------------------------------------------------ sub SYSMON_getCPUTemp_FB($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'cputemp'}) {return $map;} my $val = SYSMON_execute($hash, "ctlmgr_ctl r cpu status/StatTemperature"); if(defined($val)) { if($val=~m/(\d+),/) { my $fval = $1; my $val_txt = sprintf("%.2f", $fval); $map->{+CPU_TEMP}="$val_txt"; my $t_avg = sprintf( "%.1f", (3 * ReadingsVal($hash->{NAME},CPU_TEMP_AVG,$val_txt) + $val_txt ) / 4 ); $map->{+CPU_TEMP_AVG}="$t_avg"; $map = SYSMON_getComputeStat($hash, $map, $val_txt, CPU_TEMP."_stat"); } } return $map; } #------------------------------------------------------------------------------ # leifert CPU Frequenz (Raspberry Pi, BeagleBone Black, Cubietruck, etc.) # Sonderlocke fuer lokale Erfassung (damit die CPU nicht auf Max. gefahren wird) # Dazu darf nicht in BlockingCall und keine System-Aufrufe wie 'cat' etc. #------------------------------------------------------------------------------ sub SYSMON_getCPUFreqLocal($$;$) { my ($hash, $map, $cpuNum) = @_; if($hash->{helper}->{excludes}{'cpufreq'}) {return $map;} $cpuNum = 0 unless defined $cpuNum; my $val; if(open(my $fh, '<', "/sys/devices/system/cpu/cpu".$cpuNum."/cpufreq/scaling_cur_freq")) { $val = <$fh>; close($fh); } $val = int($val); my $val_txt = sprintf("%d", $val/1000); if($cpuNum == 0) { # aus Kompatibilitaetsgruenden $map->{+CPU_FREQ}="$val_txt"; $map = SYSMON_getComputeStat($hash, $map, $val_txt, CPU_FREQ."_stat"); } $map->{"cpu".$cpuNum."_freq"}="$val_txt"; $map = SYSMON_getComputeStat($hash, $map, $val_txt, "cpu".$cpuNum."_freq"."_stat"); return $map; } #------------------------------------------------------------------------------ # leifert CPU Frequenz (Raspberry Pi, BeagleBone Black, Cubietruck, etc.) # Nur Remote Aufrufe #------------------------------------------------------------------------------ sub SYSMON_getCPUFreq($$;$) { my ($hash, $map, $cpuNum) = @_; if($hash->{helper}->{excludes}{'cpufreq'}) {return $map;} $cpuNum = 0 unless defined $cpuNum; my $val; my $mode = $hash->{MODE}; if ($mode eq 'local') { # do nothing return $map; } # XXX Hack: Versuch zu vermeiden, dass Frequenz immer als Maximum gelesen wird #my $mode = $hash->{MODE};#AttrVal( $name, 'mode', 'local'); #if ($mode eq 'local') { # if(open(my $fh, '<', "/sys/devices/system/cpu/cpu".$cpuNum."/cpufreq/scaling_cur_freq")) { # $val = <$fh>; # close($fh); # } #} else { $val = SYSMON_execute($hash, "[ -f /sys/devices/system/cpu/cpu".$cpuNum."/cpufreq/scaling_cur_freq ] && cat /sys/devices/system/cpu/cpu".$cpuNum."/cpufreq/scaling_cur_freq 2>&1 || echo 0"); #} $val = int($val); my $val_txt = sprintf("%d", $val/1000); if($cpuNum == 0) { # aus Kompatibilitaetsgruenden $map->{+CPU_FREQ}="$val_txt"; $map = SYSMON_getComputeStat($hash, $map, $val_txt, CPU_FREQ."_stat"); } $map->{"cpu".$cpuNum."_freq"}="$val_txt"; $map = SYSMON_getComputeStat($hash, $map, $val_txt, "cpu".$cpuNum."_freq"."_stat"); return $map; } #------------------------------------------------------------------------------ # leifert CPU Frequenz fuer 2te CPU (Cubietruck, etc.) #------------------------------------------------------------------------------ #sub #SYSMON_getCPU1Freq($$) #{ # my ($hash, $map) = @_; # my $val = SYSMON_execute($hash, "cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq 2>&1"); # $val = int($val); # my $val_txt = sprintf("%d", $val/1000); # $map->{+CPU1_FREQ}="$val_txt"; # return $map; #} #------------------------------------------------------------------------------ # leifert CPU Infos (Model name & Speed in BogoMIPS) # TEST: {Dumper(SYSMON_getCPUInfo($defs{sysmon},undef))} #------------------------------------------------------------------------------ sub SYSMON_getCPUInfo($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'cpuinfo'}) {return $map;} my $old_val1 = ReadingsVal($hash->{NAME},CPU_BOGOMIPS,undef); my $old_val2 = ReadingsVal($hash->{NAME},CPU_MODEL_NAME,undef); # nur einmalig ermitteln (wird sich ja nicht aendern if(!defined($old_val1) || !defined($old_val2)) { #my @aval = SYSMON_execute($hash, "cat /proc/cpuinfo | grep 'BogoMIPS'"); my @aval = SYSMON_execute($hash, "cat /proc/cpuinfo"); foreach my $line (@aval) { my($key, $val) = split(/\s*:\s+/, $line); if(defined($key)) { if($key=~m/Processor/ || $key=~m/model name/ || $key=~m/system type/) { if($val) { $val = trim($val); $map->{+CPU_MODEL_NAME}=$val; } } elsif ($key=~m/BogoMIPS/) { if($val) { $val = trim($val); $map->{+CPU_BOGOMIPS}=$val; } } } } } else { $map->{+CPU_BOGOMIPS}=$old_val1; $map->{+CPU_MODEL_NAME}=$old_val2; } return $map; } #------------------------------------------------------------------------------ # leifert Werte aus /proc/diskstat # Werte: # 1 - major number # 2 - minor mumber # 3 - device name # Dann Datenwerte: # Field 1 -- # of reads issued # Field 2 -- # of reads merged # Field 3 -- # of sectors read # Field 4 -- # of milliseconds spent reading # Field 5 -- # of writes completed # Field 6 -- # of writes merged # Field 7 -- # of sectors written # Field 8 -- # of milliseconds spent writing # Field 9 -- # of I/Os currently in progress # Field 10 -- # of milliseconds spent doing I/Os # Field 11 -- weighted # of milliseconds spent doing I/Os # Interessant sind eigentlich "nur" Feld 2 (readin), Feld 5 (write) # Wenn es eher "um die zeit" geht, Feld 4 (reading), Feld 8 (writing), Feld 10 (Komplett) # Kleiner Hinweis, Fled 1 ist das 4. der Liste, das 3. Giebt den Namen an. # Es giebt fuer jedes Devine und jede Partition ein Eintrag. # A /proc/diskstats continuously updated and all that is necessary for us - # make measurements for "second field" and "fourth field" in two different moment of time, # receiving a difference of values and dividing it into an interval of time, # we shall have Disk I/O stats in sectors/sec. Multiply this result on 512 (number of bytes in one sector) # we shall have Disk I/O stats in bytes/sec. # # ... # https://www.kernel.org/doc/Documentation/iostats.txt # Field 1 -- # of reads completed # This is the total number of reads completed successfully. # Field 2 -- # of reads merged, field 6 -- # of writes merged # Reads and writes which are adjacent to each other may be merged for # efficiency. Thus two 4K reads may become one 8K read before it is # ultimately handed to the disk, and so it will be counted (and queued) # as only one I/O. This field lets you know how often this was done. # Field 3 -- # of sectors read # This is the total number of sectors read successfully. # Field 4 -- # of milliseconds spent reading # This is the total number of milliseconds spent by all reads (as # measured from __make_request() to end_that_request_last()). # Field 5 -- # of writes completed # This is the total number of writes completed successfully. # Field 6 -- # of writes merged # See the description of field 2. # Field 7 -- # of sectors written # This is the total number of sectors written successfully. # Field 8 -- # of milliseconds spent writing # This is the total number of milliseconds spent by all writes (as # measured from __make_request() to end_that_request_last()). # Field 9 -- # of I/Os currently in progress # The only field that should go to zero. Incremented as requests are # given to appropriate struct request_queue and decremented as they finish. # Field 10 -- # of milliseconds spent doing I/Os # This field increases so long as field 9 is nonzero. # Field 11 -- weighted # of milliseconds spent doing I/Os # This field is incremented at each I/O start, I/O completion, I/O # merge, or read of these stats by the number of I/Os in progress # (field 9) times the number of milliseconds spent doing I/O since the # last update of this field. This can provide an easy measure of both # I/O completion time and the backlog that may be accumulating. # # # Disks vs Partitions # ------------------- # # There were significant changes between 2.4 and 2.6 in the I/O subsystem. # As a result, some statistic information disappeared. The translation from # a disk address relative to a partition to the disk address relative to # the host disk happens much earlier. All merges and timings now happen # at the disk level rather than at both the disk and partition level as # in 2.4. Consequently, you'll see a different statistics output on 2.6 for # partitions from that for disks. There are only *four* fields available # for partitions on 2.6 machines. This is reflected in the examples above. # # Field 1 -- # of reads issued # This is the total number of reads issued to this partition. # Field 2 -- # of sectors read # This is the total number of sectors requested to be read from this # partition. # Field 3 -- # of writes issued # This is the total number of writes issued to this partition. # Field 4 -- # of sectors written # This is the total number of sectors requested to be written to # this partition. #------------------------------------------------------------------------------ sub SYSMON_getDiskStat($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'diskstat'}) {return $map;} my @values = SYSMON_execute($hash, "cat /proc/diskstats"); for my $entry (@values){ $map = SYSMON_getDiskStat_intern($hash, $map, $entry); #Log 3, "SYSMON-DEBUG-IOSTAT: ".$entry; } return $map; } sub SYSMON_getDiskStat_intern($$$) { my ($hash, $map, $entry) = @_; my ($d1, $d2, $pName, $nf1, $nf2, $nf3, $nf4, $nf5, $nf6, $nf7, $nf8, $nf9, $nf10, $nf11) = split(/\s+/, trim($entry)); SYSMON_Log($hash, 5, "SYSMON-DEBUG-IOSTAT: ".$pName." = ".$nf1." ".$nf2." ".$nf3." ".$nf4." ".$nf5." ".$nf6." ".$nf7." ".$nf8." ".$nf9." ".$nf10." ".$nf11); # Nur nicht-null-Werte if($nf1 eq "0") { return $map; } $pName = "io_".$pName; #Log 3, "SYSMON-DEBUG-IOSTAT: ".$pName; # Partition and 2.6-Kernel? if(defined($nf5)) { # no $map->{$pName."_raw"}=$nf1." ".$nf2." ".$nf3." ".$nf4." ".$nf5." ".$nf6." ".$nf7." ".$nf8." ".$nf9." ".$nf10." ".$nf11; } else { $map->{$pName."_raw"}=$nf1." ".$nf2." ".$nf3." ".$nf4; } #$map->{"iostat_test"}="TEST"; my $lastVal = ReadingsVal($hash->{NAME},$pName."_raw",undef); if(defined($lastVal)) { SYSMON_Log($hash,5, "SYSMON-DEBUG-IOSTAT: lastVal: $pName=".$lastVal); } if(defined $lastVal) { # Diff. ausrechnen, falls vorherigen Werte vorhanden sind. my($af1, $af2, $af3, $af4, $af5, $af6, $af7, $af8, $af9, $af10, $af11) = split(/\s+/, $lastVal); SYSMON_Log($hash,5, "SYSMON-DEBUG-IOSTAT: X: ".$pName." = ".$af1." ".$af2." ".$af3." ".$af4." ".$af5." ".$af6." ".$af7." ".$af8." ".$af9." ".$af10." ".$af11); my $sectorsRead; my $sectorsWritten; my $df1 = $nf1-$af1; my $df2 = $nf2-$af2; my $df3 = $nf3-$af3; my $df4 = $nf4-$af4; # Partition and 2.6-Kernel? if(defined($nf5)) { # no my $df5 = $nf5-$af5; my $df6 = $nf6-$af6; my $df7 = $nf7-$af7; my $df8 = $nf8-$af8; my $df9 = $nf9-$af9; my $df10 = $nf10-$af10; my $df11 = $nf11-$af11; $map->{$pName."_diff"}=$df1." ".$df2." ".$df3." ".$df4." ".$df5." ".$df6." ".$df7." ".$df8." ".$df9." ".$df10." ".$df11; $sectorsRead = $df3; $sectorsWritten = $df7; } else { $map->{$pName."_diff"}=$df1." ".$df2." ".$df3." ".$df4; $sectorsRead = $df2; $sectorsWritten = $df4; } my $sectorBytes = 512; my $BytesRead = $sectorsRead*$sectorBytes; my $BytesWritten = $sectorsWritten*$sectorBytes; # TODO: Summenwerte $map->{$pName.""}=sprintf("bytes read: %d bytes written: %d",$BytesRead, $BytesWritten); } return $map; } #------------------------------------------------------------------------------ # leifert Werte aus /proc/stat # Werte: # neuCPUuser, neuCPUnice, neuCPUsystem, neuCPUidle, neuCPUiowait, neuCPUirq, neuCPUsoftirq # Differenzberechnung: # CPUuser = neuCPUuser - altCPUuser (fuer alle anderen analog) # GesammtCPU = CPUuser + CPUnice + CPUsystem + CPUidle + CPUiowait + CPUirq + CPUsoftirq # Belastung in %: # ProzCPUuser = (CPUuser / GesammtCPU) * 100 #------------------------------------------------------------------------------ sub SYSMON_getCPUProcStat($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'cpustat'}) {return $map;} my @values = SYSMON_execute($hash, "cat /proc/stat"); for my $entry (@values){ if (index($entry, "cpu") < 0){ last; } $map = SYSMON_getCPUProcStat_intern($hash, $map, $entry); } # Wenn nur eine CPU vorhanden ist, loeschen Werte fuer CPU0 (nur Gesamt belassen) if(!defined($map->{"stat_cpu1"})){ delete $map->{"stat_cpu0"}; delete $map->{"stat_cpu0_diff"}; delete $map->{"stat_cpu0_percent"}; } return $map; } sub SYSMON_getCPUProcStat_intern($$$) { my ($hash, $map, $entry) = @_; my($tName, $neuCPUuser, $neuCPUnice, $neuCPUsystem, $neuCPUidle, $neuCPUiowait, $neuCPUirq, $neuCPUsoftirq) = split(/\s+/, trim($entry)); unless (defined $neuCPUuser) { # ingnore when not recognized return $map; } my $pName = "stat_".$tName; $map->{$pName}=$neuCPUuser." ".$neuCPUnice." ".$neuCPUsystem." ".$neuCPUidle." ".$neuCPUiowait." ".$neuCPUirq." ".$neuCPUsoftirq; my $lastVal = ReadingsVal($hash->{NAME},$pName,undef); if(defined $lastVal) { # Diff. ausrechnen, falls vorherigen Werte vorhanden sind. my($altCPUuser, $altCPUnice, $altCPUsystem, $altCPUidle, $altCPUiowait, $altCPUirq, $altCPUsoftirq) = split(/\s+/, $lastVal); my ($CPUuser, $CPUnice, $CPUsystem, $CPUidle, $CPUiowait, $CPUirq, $CPUsoftirq); if($neuCPUuser < $altCPUuser) { $CPUuser = $neuCPUuser; $CPUnice = $neuCPUnice; $CPUsystem = $neuCPUsystem; $CPUidle = $neuCPUidle; $CPUiowait = $neuCPUiowait; $CPUirq = $neuCPUirq; $CPUsoftirq = $neuCPUsoftirq; } else { $CPUuser = $neuCPUuser - $altCPUuser; $CPUnice = $neuCPUnice - $altCPUnice; $CPUsystem = $neuCPUsystem - $altCPUsystem; $CPUidle = $neuCPUidle - $altCPUidle; $CPUiowait = $neuCPUiowait - $altCPUiowait; $CPUirq = $neuCPUirq - $altCPUirq; $CPUsoftirq = $neuCPUsoftirq - $altCPUsoftirq; } $map->{$pName."_diff"}=$CPUuser." ".$CPUnice." ".$CPUsystem." ".$CPUidle." ".$CPUiowait." ".$CPUirq." ".$CPUsoftirq; my $GesammtCPU = $CPUuser + $CPUnice + $CPUsystem + $CPUidle + $CPUiowait + $CPUirq + $CPUsoftirq; my $PercentCPUuser = ($CPUuser / $GesammtCPU) * 100; my $PercentCPUnice = ($CPUnice / $GesammtCPU) * 100; my $PercentCPUsystem = ($CPUsystem / $GesammtCPU) * 100; my $PercentCPUidle = ($CPUidle / $GesammtCPU) * 100; my $PercentCPUiowait = ($CPUiowait / $GesammtCPU) * 100; my $PercentCPUirq = ($CPUirq / $GesammtCPU) * 100; my $PercentCPUsoftirq = ($CPUsoftirq / $GesammtCPU) * 100; $map->{$pName."_percent"}=sprintf ("%.2f %.2f %.2f %.2f %.2f %.2f %.2f",$PercentCPUuser,$PercentCPUnice,$PercentCPUsystem,$PercentCPUidle,$PercentCPUiowait,$PercentCPUirq,$PercentCPUsoftirq); $map->{$pName."_text"}=sprintf ("user: %.2f %%, nice: %.2f %%, sys: %.2f %%, idle: %.2f %%, io: %.2f %%, irq: %.2f %%, sirq: %.2f %%",$PercentCPUuser,$PercentCPUnice,$PercentCPUsystem,$PercentCPUidle,$PercentCPUiowait,$PercentCPUirq,$PercentCPUsoftirq); $map = SYSMON_getComputeStat($hash, $map, $PercentCPUidle, $tName."_idle_stat"); } return $map; } #------------------------------------------------------------------------------ # Liefert Werte fuer RAM und SWAP (Gesamt, Verwendet, Frei). #------------------------------------------------------------------------------ sub SYSMON_getRamAndSwap($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'ramswap'}) {return $map;} #my @speicher = qx(free -m); #my @speicher = SYSMON_execute($hash, "LANG=en free"); my $free_version = SYSMON_execute($hash, 'free -V'); $free_version =~ s/\D//g; my @speicher = SYSMON_execute($hash, 'LANG=en ' . ($free_version > 339 ? 'free -w' : 'free')); if(!@speicher) { return $map; } shift @speicher; my ($fs_desc, $total, $used, $free, $shared, $buffers, $cached); if(defined ($speicher[0])) { ($fs_desc, $total, $used, $free, $shared, $buffers, $cached) = split(/\s+/, trim($speicher[0])); } shift @speicher; my ($fs_desc2, $total2, $used2, $free2, $shared2, $buffers2, $cached2); if(defined ($speicher[0])) { ($fs_desc2, $total2, $used2, $free2, $shared2, $buffers2, $cached2) = split(/\s+/, trim($speicher[0])) } if(defined($fs_desc2)) { if($fs_desc2 ne "Swap:") { shift @speicher; if(defined ($speicher[0])) { ($fs_desc2, $total2, $used2, $free2, $shared2, $buffers2, $cached2) = split(/\s+/, trim($speicher[0])); } } } my $ram; my $swap; #my $percentage_ram; #my $percentage_swap; my $used_clean; if(defined($total) && $total > 0) { $total = $total / 1024; $used = $used / 1024; $free = $free / 1024; $buffers = $buffers / 1024; if(defined($cached)) { $cached = $cached / 1024; } else { # Bei FritzBox wird dieser Wert nicht ausgegeben $cached = 0; } #$used_clean = $used - $buffers - $cached; #$ram = sprintf("Total: %.2f MB, Used: %.2f MB, %.2f %%, Free: %.2f MB", $total, $used_clean, ($used_clean / $total * 100), ($free + $buffers + $cached)); #if ($total > 2048) { if ($free_version > 339) { $used_clean = $used; $ram = sprintf("Total: %.2f MB, Used: %.2f MB, %.2f %%, Free: %.2f MB", $total, $used_clean, ($used_clean / $total * 100), ($free)); } else { $used_clean = $used - $buffers - $cached; $ram = sprintf("Total: %.2f MB, Used: %.2f MB, %.2f %%, Free: %.2f MB", $total, $used_clean, ($used_clean / $total * 100), ($free + $buffers + $cached)); } } else { $ram = "n/a"; } $map->{+RAM} = $ram; $map = SYSMON_getComputeStat($hash, $map, $used_clean, "ram_used_stat"); # wenn kein swap definiert ist, ist die Groesse (total2) gleich Null. Dies wuerde eine Exception (division by zero) ausloesen if(defined($total2) && $total2 > 0 && defined($used2) && defined($free2)) { $total2 = $total2 / 1024; $used2 = $used2 / 1024; $free2 = $free2 / 1024; $swap = sprintf("Total: %.2f MB, Used: %.2f MB, %.2f %%, Free: %.2f MB", $total2, $used2, ($used2 / $total2 * 100), $free2); } else { $swap = "n/a" } $map->{+SWAP} = $swap; $map = SYSMON_getComputeStat($hash, $map, $used2, "swap_used_stat"); return $map; } #------------------------------------------------------------------------------ # Prueft, ob das Host-System OSX ist (darvin). #------------------------------------------------------------------------------ sub SYSMON_isOSX() { return $^O eq 'darwin'; } #------------------------------------------------------------------------------ # Prueft, ob das Host-System Linux ist (linux). #------------------------------------------------------------------------------ sub SYSMON_isLinux() { return $^O eq 'linux'; } #------------------------------------------------------------------------------ # Liefert Werte fuer RAM und SWAP (Gesamt, Verwendet, Frei). #------------------------------------------------------------------------------ sub SYSMON_getRamAndSwapOSX($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'ramswap'}) {return $map;} my $debug = 0; # Nur zum Testen! #my @speicher = qx(free -m); my @amemsize = SYSMON_execute($hash, "sysctl hw.memsize"); if($debug) { @amemsize = ("hw.memsize: 8589934592"); } if($amemsize[0]=~m/hw.memsize:\s+(.+)/) { my $total = $1; my @avmstat = SYSMON_execute($hash, "vm_stat"); if($debug) { @avmstat = ('Mach Virtual Memory Statistics: (page size of 4096 bytes)', 'Pages free: 5268.', 'Pages active: 440314.', 'Pages inactive: 430905.', 'Pages speculative: 878.', 'Pages throttled: 0.', 'Pages wired down: 398445.', 'Pages purgeable: 69.', '"Translation faults": 508984629.', 'Pages copy-on-write: 5668036.', 'Pages zero filled: 347281743.', 'Pages reactivated: 114745855.', 'Pages purged: 13495647.', 'File-backed pages: 88747.', 'Anonymous pages: 783350.', 'Pages stored in compressor: 1760568.', 'Pages occupied by compressor: 820444.', 'Decompressions: 48558417.', 'Compressions: 63022425.', 'Pageins: 3754238.', 'Pageouts: 589840.', 'Swapins: 714378.', 'Swapouts: 1017813.'); } #wired down, active, inactive my $wired_down=0; my $active=0; my $inactive=0; my $blockSize = 4096; foreach my $k (@avmstat) { if($k=~m/page\s+size\s+of\s+(\d+)\s+bytes/) { $blockSize = $1; } if($k=~m/Pages\s+wired\s+down:\s+(.+)\./) { $wired_down = $1; } if($k=~m/Pages\s+active:\s+(.+)\./) { $active = $1; } if($k=~m/Pages\s+inactive:\s+(.+)\./) { $inactive = $1; } } $wired_down = $wired_down * $blockSize / 1048576; # In Megabyte umrechnen $active = $active * $blockSize / 1048576; $inactive = $inactive * $blockSize / 1048576; my $used = $wired_down+$active+$inactive; $total = $total/1048576; my $free = $total-$used; my $ram = sprintf("Total: %.2f MB, Used: %.2f MB, %.2f %%, Free: %.2f MB", $total, $used , ($used / $total * 100), $free); #Log 3, "SYSMON >>>>>>>>>>>>>>>>>>>>>>>>> OSX: RAM: ".$ram; $map->{+RAM} = $ram; $map = SYSMON_getComputeStat($hash, $map, $used, "ram_used_stat"); my @avm = SYSMON_execute($hash, "sysctl vm.swapusage"); if($debug) { @avm=( #'vm.loadavg: { 2.45 2.19 3.34 }', 'vm.swapusage: total = 1024.00M used = 529.25M free = 494.75M (encrypted)', #'vm.cs_force_kill: 0', #'vm.cs_force_hard: 0', #'vm.cs_debug: 0', #'vm.cs_all_vnodes: 0', #'vm.cs_enforcement: 0', #'vm.cs_enforcement_panic: 0', #'vm.sigpup_disable: 0', #'vm.global_no_user_wire_amount: 67108864', #'vm.global_user_wire_limit: 8522825728', #'vm.user_wire_limit: 8522825728', #'vm.vm_copy_src_not_internal: 129', #'vm.vm_copy_src_not_symmetric: 14994', #'vm.vm_copy_src_large: 0', #'vm.vm_page_external_count: 355255', #'vm.vm_page_filecache_min: 104857', #'vm.compressor_mode: 4', #'vm.compressor_bytes_used: 2984467096', #'vm.compressor_swapout_target_age: 0', #'vm.compressor_eval_period_in_msecs: 250', #'vm.compressor_sample_min_in_msecs: 500', #'vm.compressor_sample_max_in_msecs: 10000', #'vm.compressor_thrashing_threshold_per_10msecs: 50', #'vm.compressor_thrashing_min_per_10msecs: 20', #'vm.compressor_minorcompact_threshold_divisor: 20', #'vm.compressor_majorcompact_threshold_divisor: 25', #'vm.compressor_unthrottle_threshold_divisor: 35', #'vm.compressor_catchup_threshold_divisor: 50', #'vm.cs_validation: 1', #'vm.cs_blob_count: 616', #'vm.cs_blob_size: 8053170', #'vm.cs_blob_count_peak: 693', #'vm.cs_blob_size_peak: 8389641', #'vm.cs_blob_size_max: 1675264', #'vm.vm_debug_events: 0', #'vm.allow_stack_exec: 0', #'vm.allow_data_exec: 1', #'vm.shared_region_unnest_logging: 1', #'vm.shared_region_trace_level: 1', #'vm.shared_region_version: 3', #'vm.shared_region_persistence: 0', #'vm.vm_page_free_target: 2000', #'vm.memory_pressure: 0', #'vm.page_free_wanted: 86', #'vm.page_purgeable_count: 1055', #'vm.page_purgeable_wired_count: 0', #'vm.madvise_free_debug: 0', #'vm.page_reusable_count: 39048', #'vm.reusable_success: 11350536', #'vm.reusable_failure: 1060241', #'vm.reusable_shared: 248771', #'vm.all_reusable_calls: 290574', #'vm.partial_reusable_calls: 11142306', #'vm.reuse_success: 9593371', #'vm.reuse_failure: 5124', #'vm.all_reuse_calls: 257820', #'vm.partial_reuse_calls: 9684238', #'vm.can_reuse_success: 6171792', #'vm.can_reuse_failure: 79183', #'vm.reusable_reclaimed: 0', #'vm.page_free_count: 1914', #'vm.page_speculative_count: 810', #'vm.page_cleaned_count: 0', #'vm.pageout_inactive_dirty_internal: 63170734', #'vm.pageout_inactive_dirty_external: 465495', #'vm.pageout_inactive_clean: 18967682', #'vm.pageout_speculative_clean: 32929182', #'vm.pageout_inactive_used: 115155398', #'vm.pageout_freed_from_inactive_clean: 18423099', #'vm.pageout_freed_from_speculative: 32929182', #'vm.pageout_freed_from_cleaned: 568334', #'vm.pageout_enqueued_cleaned: 1010912', #'vm.pageout_enqueued_cleaned_from_inactive_clean: 0', #'vm.pageout_enqueued_cleaned_from_inactive_dirty: 1011010', #'vm.pageout_cleaned: 568334', #'vm.pageout_cleaned_reactivated: 407922', #'vm.pageout_cleaned_reference_reactivated: 4', #'vm.pageout_cleaned_volatile_reactivated: 0', #'vm.pageout_cleaned_fault_reactivated: 557', #'vm.pageout_cleaned_commit_reactivated: 407361', #'vm.pageout_cleaned_busy: 33', #'vm.pageout_cleaned_nolock: 12931' ); } #vm.swapusage: total = 1024.00M used = 529.25M free = 494.75M (encrypted) if($avm[0]=~m/vm.swapusage:\s+total\s+=\s+(\S*)\s+used\s+=\s+(\S*)\s+free\s+=\s+(\S*)\s+(.*)/) { my $total2 = SYSMON_fmtStorageAmount_($1); my $used2 = SYSMON_fmtStorageAmount_($2); my $free2 = SYSMON_fmtStorageAmount_($3); my $swap = sprintf("Total: %.2f MB, Used: %.2f MB, %.2f %%, Free: %.2f MB", $total2, $used2, ($used2 / $total2 * 100), $free2); $map->{+SWAP} = $swap; $map = SYSMON_getComputeStat($hash, $map, $used2, "swap_used_stat"); #Log 3, "SYSMON >>>>>>>>>>>>>>>>>>>>>>>>> OSX: SWAP: ".$swap; } } return $map; } sub SYSMON_fmtStorageAmount_($) { my ($t) = @_; if($t=~m/([\d|\.]+)(.*)/) { my $r=$1; my $m=$2; if($m) { # Modifier testen if($m eq 'M') { # Megabyte ist OK,so lassen return $r; } if($m eq 'G') { # Gigabyte: in MB umwandeln $r=$r*1024; } # K, oder P nehmen ich nicht mehr bzw. noch nicht an ;) return $r; } } return $t; } #------------------------------------------------------------------------------ # Liefert Fuellstand fuer das angegebene Dateisystem (z.B. '/dev/root', '/dev/sda1' (USB stick)). # Eingabeparameter: HASH; MAP; FS-Bezeichnung #------------------------------------------------------------------------------ sub SYSMON_getFileSystemInfo ($$$) { my ($hash, $map, $fs) = @_; if($hash->{helper}->{excludes}{'filesystem'}) {return $map;} SYSMON_Log($hash, 5, "get $fs"); # Neue Syntax: benannte Filesystems: : my($fName, $fDef, $fComment) = split(/:/, $fs); if(defined $fDef) { $fs = $fDef; } #my $disk = "df ".$fs." -m 2>&1"; # in case of failure get string from stderr my $disk = "df ".$fs." -m 2>/dev/null"; SYSMON_Log($hash, 5, "exec $disk"); #my @filesystems = qx($disk); my @filesystems = SYSMON_execute($hash, $disk); SYSMON_Log($hash, 5, "recieved ".scalar(scalar(@filesystems))." lines"); # - DEBUG - #if($fs eq "/test") { # @filesystems=( # "Filesystem 1M-blocks Used Available Use% Mounted on", # "/dev/mapper/n40l-root", # " 226741 22032 193192 11% /" # ); # $fs = "/"; #} #- DEBUG - #if(!defined @filesystems) { return $map; } # Ausgabe leer #if(scalar(@filesystems) == 0) { return $map; } # Array leer if(defined($filesystems[0])) { SYSMON_Log($hash, 5, "recieved line0 $filesystems[0]"); } else { SYSMON_Log($hash, 5, "recieved empty line"); } shift @filesystems; # Falls kein Eintrag gefunden (z.B: kein Medium im Laufwerk), mit Nullen fuellen (damit die Plots richtig funktionieren). if(defined $fDef) { $map->{$fName} = "Total: 0 MB, Used: 0 MB, 0 %, Available: 0 MB at ".$fs." (not available)"; } else { $map->{+FS_PREFIX.$fs} = "Total: 0 MB, Used: 0 MB, 0 %, Available: 0 MB at ".$fs." (not available)"; } #return $map unless defined(@filesystems); return $map unless int(@filesystems)>0; #if(!defined $filesystems[0]) { return $map; } # Ausgabe leer SYSMON_Log($hash, 5, "analyse line $filesystems[0] for $fs"); #if (!($filesystems[0]=~ /$fs\s*$/)){ shift @filesystems; } if (!($filesystems[0]=~ /$fs$/)){ shift @filesystems; if(int(@filesystems)>0) { SYSMON_Log($hash, 5, "analyse line $filesystems[0] for $fs"); } } else { SYSMON_Log($hash, 5, "pattern ($fs) found"); } #if (index($filesystems[0], $fs) < 0) { shift @filesystems; } # Wenn die Bezeichnung so lang ist, dass die Zeile umgebrochen wird... #if (index($filesystems[0], $fs) >= 0) # check if filesystem available -> gives failure on console if (int(@filesystems)>0 && $filesystems[0]=~ /$fs$/) { SYSMON_Log($hash, 5, "use line $filesystems[0]"); my ($fs_desc, $total, $used, $available, $percentage_used, $mnt_point) = split(/\s+/, $filesystems[0]); $percentage_used =~ /^(.+)%$/; $percentage_used = $1; my $out_txt = "Total: ".$total." MB, Used: ".$used." MB, ".$percentage_used." %, Available: ".$available." MB at ".$mnt_point; if(defined $fDef) { $map->{$fName} = $out_txt; } else { $map->{+FS_PREFIX.$mnt_point} = $out_txt; } } # else { # if(defined $fDef) { # $map->{$fName} = "not available"; # } else { # $map->{+FS_PREFIX.$fs} = "not available"; # } #} return $map; } #------------------------------------------------------------------------------ # Liefert Netztwerkinformationen # Parameter: HASH; MAP; DEVICE (eth0 or wlan0) #------------------------------------------------------------------------------ sub SYSMON_getNetworkInfo ($$$) { my ($hash, $map, $device) = @_; if($hash->{helper}->{excludes}{'network'}) {return $map;} return $map unless (-e "/sbin/ifconfig"); SYSMON_Log($hash, 5, "get $device"); my($nName, $nDef) = split(/:/, $device); if(!defined $nDef) { $nDef = $nName; } $device = $nDef; # in case of network not present get failure from stderr (2>&1) my $cmd="/sbin/ifconfig ".$device." 2>&1"; #my @dataThroughput = qx($cmd); my @dataThroughput = SYSMON_execute($hash, $cmd); SYSMON_Log ($hash, 5, "SYSMON_getNetworkInfo>>>>>>>>>>>>>>>>".Dumper(@dataThroughput)); #--- DEBUG --- if($device eq "_test1") { @dataThroughput = ( "enp4s0: flags=4163 mtu 1492", " inet 192.168.2.7 netmask 255.255.255.0 broadcast 192.168.2.255", " ether 00:21:85:5a:0d:e0 txqueuelen 1000 (Ethernet)", " RX packets 1553313 bytes 651891540 (621.6 MiB)", " RX errors 0 dropped 0 overruns 0 frame 0", " TX packets 1915387 bytes 587386206 (560.1 MiB)", " TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0"); } if($device eq "_test2") { @dataThroughput = ( "eth0 Link encap:Ethernet Hardware Adresse b8:27:eb:47:a9:8d", " inet Adresse:192.168.2.118 Bcast:192.168.2.255 Maske:255.255.255.0", " inet6-Adresse: 2003:46:b6b:3100:ba27:ebff:fe47:a98d/64 Gültigkeitsbereich:Global", " inet6-Adresse: fe80::ba27:ebff:fe47:a98d/64 Gültigkeitsbereich:Verbindung", " UP BROADCAST RUNNING MULTICAST MTU:1500 Metrik:1", " RX packets:1224709 errors:0 dropped:0 overruns:0 frame:0", " TX packets:1156620 errors:0 dropped:0 overruns:0 carrier:0", " Kollisionen:0 Sendewarteschlangenlänge:1000", " RX bytes:180806073 (172.4 MiB) TX bytes:108919337 (103.8 MiB)"); } #--- DEBUG --- # check if network available if (defined($dataThroughput[0]) && index($dataThroughput[0], 'Fehler') < 0 && index($dataThroughput[0], 'error') < 0) { #Log 3, "SYSMON>>>>>>>>>>>>>>>>> OK >>>".$dataThroughput[0]; my $dataThroughput = undef; # Suche nach der Daten in Form: # eth0 Link encap:Ethernet Hardware Adresse b8:27:eb:a5:e0:85 # inet Adresse:192.168.0.10 Bcast:192.168.0.255 Maske:255.255.255.0 # UP BROADCAST RUNNING MULTICAST MTU:1500 Metrik:1 # RX packets:339826 errors:0 dropped:45 overruns:0 frame:0 # TX packets:533293 errors:0 dropped:0 overruns:0 carrier:0 # Kollisionen:0 Sendewarteschlangenlaenge:1000 # RX bytes:25517384 (24.3 MiB) TX bytes:683970999 (652.2 MiB) my $ip = undef; my $ip6 = undef; foreach (@dataThroughput) { if($_=~ m/inet\s+(Adresse:)*(\S*)/) { $ip=$2; } if(!$ip && $_=~ m/inet\s+(addr:)*(\S*)/) { $ip=$2; SYSMON_Log ($hash, 3, "SYSMON_getNetworkInfo:ip: ".$ip); } if($_=~ m/inet6-(Adresse:)*\s*(\S*)\s+G.ltigkeitsbereich:Verbindung/) { $ip6=$2; } if(!$ip && $_=~ m/inet6\s+(addr:)*\s*(\S*)\s+Scope:Link/) { $ip6=$2; } if(!$ip && $_=~ m/inet6\s+(addr:)*\s*(\S*)/) { $ip6=$2; } if(index($_, 'RX bytes') >= 0) { $dataThroughput = $_; last; } } if(defined $ip) { $ip =~ s/addr://; $map->{$nName.IP_SUFFIX} = $ip; } if(defined $ip6) { $ip6 =~ s/addr://; $map->{$nName.IP6_SUFFIX} = $ip6; } my $rxRaw = -1; my $txRaw = -1; # if(-e "/sys/class/net/$nName/statistics/rx_bytes" && -e "/sys/class/net/$nName/statistics/tx_bytes") { if(SYSMON_isNetStatClass($hash, $nName)) { $rxRaw = SYSMON_execute($hash, "cat /sys/class/net/$nName/statistics/rx_bytes"); $rxRaw = -1 unless (defined($rxRaw) && looks_like_number($rxRaw)); $txRaw = SYSMON_execute($hash, "cat /sys/class/net/$nName/statistics/tx_bytes"); $txRaw = -1 unless (defined($txRaw) && looks_like_number($txRaw)); } if($rxRaw<0||$txRaw<0) { if(defined $dataThroughput) { # remove RX bytes or TX bytes from string $dataThroughput =~ s/RX bytes://; $dataThroughput =~ s/TX bytes://; $dataThroughput = trim($dataThroughput); @dataThroughput = split(/ /, $dataThroughput); # return of split is array $rxRaw = $dataThroughput[0] if(defined $dataThroughput[0]); $txRaw = $dataThroughput[4] if(defined $dataThroughput[4]); } else { # # an manchen Systemen kann die Ausgabe leider auch anders aussehen: # enp4s0: flags=4163 mtu 1492 # inet 192.168.2.7 netmask 255.255.255.0 broadcast 192.168.2.255 # ether 00:21:85:5a:0d:e0 txqueuelen 1000 (Ethernet) # RX packets 1553313 bytes 651891540 (621.6 MiB) # RX errors 0 dropped 0 overruns 0 frame 0 # TX packets 1915387 bytes 587386206 (560.1 MiB) # TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 # my $d; foreach $d (@dataThroughput) { if($d =~ m/RX\s.*\sbytes\s(\d*)\s/) { $rxRaw = $1; } if($d =~ m/TX\s.*\sbytes\s(\d*)\s/) { $txRaw = $1; } } } } if($rxRaw<0) { # Daten nicht gefunden / Format unbekannt $map->{$nName} = "unexpected format"; $map->{$nName.DIFF_SUFFIX} = "unexpected format"; } else { $map->{$nName."_rx"} = $rxRaw; $map->{$nName."_tx"} = $txRaw; $rxRaw = $rxRaw / 1048576; # Bytes in MB $txRaw = $txRaw / 1048576; my $rx = sprintf ("%.2f", $rxRaw); my $tx = sprintf ("%.2f", $txRaw); my $totalRxTx = $rx + $tx; my $out_txt = "RX: ".$rx." MB, TX: ".$tx." MB, Total: ".$totalRxTx." MB"; $map->{$nName} = $out_txt; my $lastVal = ReadingsVal($hash->{NAME},$nName,"RX: 0 MB, TX: 0 MB, Total: 0 MB"); my ($d0, $o_rx, $d1, $d2, $o_tx, $d3, $d4, $o_tt, $d5) = split(/\s+/, trim($lastVal)); if(defined($o_tx) && defined($o_tt)) { my $d_rx = $rx-$o_rx; if($d_rx<0) {$d_rx=0;} my $d_tx = $tx-$o_tx; if($d_tx<0) {$d_tx=0;} my $d_tt = $totalRxTx-$o_tt; if($d_tt<0) {$d_tt=0;} my $out_txt_diff = "RX: ".sprintf ("%.2f", $d_rx)." MB, TX: ".sprintf ("%.2f", $d_tx)." MB, Total: ".sprintf ("%.2f", $d_tt)." MB"; $map->{$nName.DIFF_SUFFIX} = $out_txt_diff; } my $speed; #if ($nName eq "wlan0") { if($nName=~m/wlan/) { #my @iwData = SYSMON_execute($hash, "/sbin/iwconfig $nName 2>/dev/null"); my @iwData = SYSMON_execute($hash, "/sbin/iwconfig $nDef 2>/dev/null"); foreach (@iwData) { next unless ($_); if($_=~ m/Bit\sRate+(=|:)*(\S*)/) { $speed=$2; } } } elsif (1 eq SYSMON_execute($hash, "[ -f /sys/class/net/$nName/speed ] && echo 1 || echo 0")) { $speed = SYSMON_execute($hash, "cat /sys/class/net/$nName/speed 2>/dev/null"); } else { $speed = "not available"; } if(defined($speed)) { $map->{$nName.SPEED_SUFFIX} = $speed; } } } else { #Log 3, "SYSMON>>>>>>>>>>>>>>>>> NOK "; #Log 3, "SYSMON>>>>>>>>>>>>>>>>> >>> ".$nName; $map->{$nName} = "not available"; $map->{$nName.DIFF_SUFFIX} = "not available"; } return $map; } #------------------------------------------------------------------------------ # Liefert Informationen, ob WLAN an oder aus ist (nur FritzBox) # Parameter: HASH; MAP #------------------------------------------------------------------------------ sub SYSMON_getFBWLANState($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'network'}) {return $map;} #SYSMON_Log($hash, 5, ""); $map->{+FB_WLAN_STATE}=SYSMON_acquireInfo_intern($hash, "ctlmgr_ctl r wlan settings/ap_enabled",1); return $map; } #------------------------------------------------------------------------------ # Liefert Informationen, ob WLAN-Gastzugang an oder aus ist (nur FritzBox) # Parameter: HASH; MAP #------------------------------------------------------------------------------ sub SYSMON_getFBWLANGuestState($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'network'}) {return $map;} #SYSMON_Log($hash, 5, ""); $map->{+FB_WLAN_GUEST_STATE}=SYSMON_acquireInfo_intern($hash, "ctlmgr_ctl r wlan settings/guest_ap_enabled",1); return $map; } #------------------------------------------------------------------------------ # Liefert IP Adresse im Internet (nur FritzBox) # Parameter: HASH; MAP #------------------------------------------------------------------------------ sub SYSMON_getFBInetIP($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'network'}) {return $map;} $map->{+FB_INET_IP}=SYSMON_acquireInfo_intern($hash, "ctlmgr_ctl r dslstatistic status/ifacestat0/ipaddr"); return $map; } #------------------------------------------------------------------------------ # Liefert Status Internet-Verbindung (nur FritzBox) # Parameter: HASH; MAP #------------------------------------------------------------------------------ sub SYSMON_getFBInetConnectionState($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'network'}) {return $map;} $map->{+FB_INET_STATE}=SYSMON_acquireInfo_intern($hash, "ctlmgr_ctl r dslstatistic status/ifacestat0/connection_status"); return $map; } #------------------------------------------------------------------------------ # Liefert Status Klingelsperre (nur FritzBox) # Parameter: HASH; MAP #------------------------------------------------------------------------------ sub SYSMON_getFBNightTimeControl($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fbnightctrl'}) {return $map;} $map->{+FB_N_TIME_CTRL}=SYSMON_acquireInfo_intern($hash, "ctlmgr_ctl r box settings/night_time_control_enabled",1); return $map; } #------------------------------------------------------------------------------ # Liefert Anzahl der nicht abgehoerten Nachrichten auf dem Anrufbeantworter (nur FritzBox) # Parameter: HASH; MAP #------------------------------------------------------------------------------ sub SYSMON_getFBNumNewMessages($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fbnewmessages'}) {return $map;} $map->{+FB_NUM_NEW_MESSAGES}=SYSMON_acquireInfo_intern($hash, "ctlmgr_ctl r tam status/NumNewMessages"); return $map; } #------------------------------------------------------------------------------ # Liefert DECT-Temperatur einer FritzBox. # Parameter: HASH; MAP #------------------------------------------------------------------------------ sub SYSMON_getFBDECTTemp($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fbdecttemp'}) {return $map;} $map->{+FB_DECT_TEMP}=SYSMON_acquireInfo_intern($hash, "ctlmgr_ctl r dect status/Temperature"); return $map; } #------------------------------------------------------------------------------ # Liefert Liste an der FritzBox bekannter Devices. # Parameter: HASH # Return Hash mit Devices #------------------------------------------------------------------------------ sub SYSMON_getFBLanDeviceList($) { my ($hash) = @_; if(!SYSMON_isFB($hash)) { return undef; } my $map; my $name = $hash->{NAME}; # --- #TODO: SSH my $msg = undef; my $openedTelnet = 0; my $telnet = $hash->{".telnet"}; #$telnet = undef; my $mode = $hash->{MODE}; # Wenn remote: open connection if ($mode eq 'telnet') { unless (defined $telnet) { SYSMON_Log($hash, 5, "$name: Open single telnet connection"); $msg = SYSMON_Open_Connection($hash); $hash->{helper}{error_msg}=$msg; if (!$msg) { $openedTelnet = 1; $hash->{helper}{error_msg}=undef; } } } # --- my $count = SYSMON_execute($hash, "ctlmgr_ctl r landevice settings/landevice/count"); if(defined($count)) { for (my $i=0;$i<$count;$i++) { #landevice0/... # ip=192.168.178.12, mac=00:1F:3F:MM:AA:CC, name=PC-192-168-178-12, manu_name=0, # dhcp=0, static_dhcp=0, wlan=0, ethernet=1, active=1, online=0, speed=100, # deleteable=2, wakeup=0, source=4096, neighbour_name=, is_double_neighbour_name=0 # ipv6addrs=, ipv6_ifid= my $dev_name = SYSMON_execute($hash, "ctlmgr_ctl r landevice settings/landevice".$i."/name"); my $dev_ip = SYSMON_execute($hash, "ctlmgr_ctl r landevice settings/landevice".$i."/ip"); my $dev_mac = SYSMON_execute($hash, "ctlmgr_ctl r landevice settings/landevice".$i."/mac"); my $dev_active = SYSMON_execute($hash, "ctlmgr_ctl r landevice settings/landevice".$i."/active"); $map->{$dev_name}{id} = $i; $map->{$dev_name}{name} = $dev_name; $map->{$dev_name}{ip} = $dev_ip; $map->{$dev_name}{mac} = $dev_mac; $map->{$dev_name}{active} = $dev_active; } } # --- # Wenn remote: close connection if ($mode eq 'telnet') { if($openedTelnet) { SYSMON_Log($hash, 5, "$name: Close shared telnet connection"); SYSMON_Close_Connection( $hash ); } } # --- return $map; } # TODO: FritzBox-Infos: Dateien /var/env oder /proc/sys/urlader/environment. #------------------------------------------------------------------------------ # Liefert Informationen zu verschiedenen Eigenschaften durch Aufruf von entsprechenden Befehlen # Parameter: HASH; cmd; Art (Interpretieren als: 1=on/off) #------------------------------------------------------------------------------ sub SYSMON_acquireInfo_intern($$;$) { my ($hash, $cmd, $art) = @_; SYSMON_Log($hash, 5, "cmd: ".$cmd); my $str = SYSMON_execute($hash, $cmd); if(defined($str)) { $str = trim($str); } my $ret; if(!defined($art)) { $art= 0; } $ret = $str; no warnings; if($art == 1) { if($str+0 == 1) { $ret="on"; } else { if($str+0 == 0) { $ret="off"; } else { $ret="unknown"; } } } use warnings; return $ret; } sub SYSMON_FBVersionInfo($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fbversion'}) {return $map;} my @ar = SYSMON_execute($hash, "/etc/version --version --date"); my $data = $ar[0]; my($v, $d, $t) = split(/\s+/, $data); my $version = ""; if(defined($v)) { $version = $v; } if(defined($d)) { $version.= " ".$d; } if(defined($t)) { $version.= " ".$t; } #if(defined($data[0])) { # #Version # $version = $data[0]; #} #if(defined($data[1])) { # #Date # $version = $version." ".$data[1]; #} if($version ne "") { $map->{+FB_FW_VERSION}=$version; } return $map; } #DSL-Downstream und DSL-Upstream abfragen sub SYSMON_getFBStreamRate($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fbdsl'}) {return $map;} my $ds_rate = SYSMON_execute($hash, "ctlmgr_ctl r sar status/dsl_ds_rate"); unless($ds_rate) { return SYSMON_getFBStreamRate2($hash, $map); } my $us_rate = SYSMON_execute($hash, "ctlmgr_ctl r sar status/dsl_us_rate"); if($ds_rate ne "" && $us_rate ne "") { $map->{+FB_DSL_RATE}="down: ".int($ds_rate)." kBit/s, up: ".int($us_rate)." kBit/s"; } return $map; } # DSL-Geschwindigkeit mit neuer FritzOS (6.23) sub SYSMON_getFBStreamRate2($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fbdsl'}) {return $map;} my $ds_rate = SYSMON_execute($hash, "ctlmgr_ctl r dslstatglobal status/in"); my $us_rate = SYSMON_execute($hash, "ctlmgr_ctl r dslstatglobal status/out"); if(defined($ds_rate) && defined($us_rate) && $ds_rate ne "" && $us_rate ne "") { $ds_rate = $ds_rate/1000; $us_rate = $us_rate/1000; $map->{+FB_DSL_RATE}="down: ".int($ds_rate)." kBit/s, up: ".int($us_rate)." kBit/s"; } return $map; } # Ausrechnet aus der Zahl der Sekunden Anzeige in Tagen:Stunden:Minuten:Sekunden. sub SYSMON_sec2Dauer($){ my ($t) = @_; my $d = int($t/86400); my $r = $t-($d*86400); my $h = int($r/3600); $r = $r - ($h*3600); my $m = int($r/60); my $s = $r - $m*60; return sprintf("%02d Tage %02d Std. %02d Min. %02d Sec.",$d,$h,$m,$s); } #Sync-Zeit mit Vermittlungsstelle abfragen sub SYSMON_getFBSyncTime($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fbdsl'}) {return $map;} my $data = SYSMON_execute($hash, "ctlmgr_ctl r sar status/modem_ShowtimeSecs"); unless($data) { return SYSMON_getFBSyncTime2($hash, $map); } if($data ne "") { my $idata = int($data); $map->{+FB_DSL_SYNCTIME}=SYSMON_sec2Dauer($idata); } return $map; } #Sync-Zeit mit Vermittlungsstelle abfragen mit neuer FritzOS (6.23) sub SYSMON_getFBSyncTime2($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fbdsl'}) {return $map;} my $data = SYSMON_execute($hash, "ctlmgr_ctl r dslstatistic status/ifacestat0/connect_time"); if(defined($data) && $data ne "") { $map->{+FB_DSL_SYNCTIME}=$data; } return $map; } #Uebertragungsfehler abfragen (nicht behebbar und behebbar) sub SYSMON_getFBCRCFEC($$) { my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'fbdsl'}) {return $map;} my $ds_crc = SYSMON_execute($hash, "ctlmgr_ctl r sar status/ds_crc_per15min"); my $us_crc = SYSMON_execute($hash, "ctlmgr_ctl r sar status/us_crc_per15min"); my $ds_fec = SYSMON_execute($hash, "ctlmgr_ctl r sar status/ds_fec_per15min"); my $us_fec = SYSMON_execute($hash, "ctlmgr_ctl r sar status/us_fec_per15min"); if(defined($ds_crc) && $ds_crc ne "") { # FB_DSL_CRC_15 $map->{+FB_DSL_CRC_15}="down: ".int($ds_crc)." up: ".int($us_crc); } if(defined($ds_fec) && $ds_fec ne "") { # FB_DSL_FEC_15 $map->{+FB_DSL_FEC_15}="down: ".int($ds_fec)." up: ".int($us_fec); } return $map; } #------------------------------------------------------------------------------ # Systemparameter als HTML-Tabelle ausgeben # Parameter: Name des SYSMON-Geraetes (muss existieren, kann auch anderer Modul genutzt werden), dessen Daten zur Anzeige gebracht werden sollen. # (optional) Liste der anzuzeigenden Werte (ReadingName[:Comment:[Postfix[:FormatString]]],...) # Beispiel: define sysv weblink htmlCode {SYSMON_ShowValuesHTML('sysmon', ('date:Datum', 'cpu_temp:CPU Temperatur: °C', 'cpu_freq:CPU Frequenz: MHz'))} #------------------------------------------------------------------------------ sub SYSMON_ShowValuesHTML ($;@) { my ($name, @data) = @_; return SYSMON_ShowValuesFmt($name, undef, 1, @data); } #------------------------------------------------------------------------------ # Systemparameter als HTML-Tabelle ausgeben. Zusaetzlich wird eine Ueberschrift ausgegeben. # Parameter: Name des SYSMON-Geraetes (muss existieren, kann auch anderer Modul genutzt werden), dessen Daten zur Anzeige gebracht werden sollen. # Title: Ueberschrift (Text) # (optional) Liste der anzuzeigenden Werte (ReadingName[:Comment:[Postfix[:FormatString]]],...) # Beispiel: define sysv weblink htmlCode {SYSMON_ShowValuesHTML('sysmon', ('date:Datum', 'cpu_temp:CPU Temperatur: °C', 'cpu_freq:CPU Frequenz: MHz'))} #------------------------------------------------------------------------------ sub SYSMON_ShowValuesHTMLTitled ($;$@) { my ($name, $title, @data) = @_; $title = $attr{$name}{'alias'} unless $title; $title = $name unless $title; return SYSMON_ShowValuesFmt($name, $title, 1, @data); } #------------------------------------------------------------------------------ # Systemparameter im Textformat ausgeben # Parameter: Name des SYSMON-Geraetes (muss existieren, kann auch anderer Modul genutzt werden), dessen Daten zur Anzeige gebracht werden sollen. # (optional) Liste der anzuzeigenden Werte (ReadingName[:Comment:[Postfix[:FormatString]]],...) # Beispiel: define sysv weblink htmlCode {SYSMON_ShowValuesText('sysmon', ('date:Datum', 'cpu_temp:CPU Temperatur: °C', 'cpu_freq:CPU Frequenz: MHz'))} #------------------------------------------------------------------------------ sub SYSMON_ShowValuesText ($;@) { my ($name, @data) = @_; return SYSMON_ShowValuesFmt($name, undef, 0, @data); } #------------------------------------------------------------------------------ # Systemparameter im Textformat ausgeben # Parameter: Name des SYSMON-Geraetes (muss existieren, kann auch anderer Modul genutzt werden), dessen Daten zur Anzeige gebracht werden sollen. # Title: Ueberschrift (Text) # (optional) Liste der anzuzeigenden Werte (ReadingName[:Comment:[Postfix[:FormatString]]],...) # Beispiel: define sysv weblink htmlCode {SYSMON_ShowValuesText('sysmon', ('date:Datum', 'cpu_temp:CPU Temperatur: °C', 'cpu_freq:CPU Frequenz: MHz'))} #------------------------------------------------------------------------------ sub SYSMON_ShowValuesTextTitled ($;$@) { my ($name, $title, @data) = @_; $title = $attr{$name}{'alias'} unless $title; $title = $name unless $title; return SYSMON_ShowValuesFmt($name, $title, 0, @data); } #------------------------------------------------------------------------------ # Systemparameter formatiert ausgeben # Parameter: # Name des SYSMON-Geraetes (muss existieren), dessen Daten zur Anzeige gebracht werden sollen. # Title: Ueberschrift # Format: 0 = Text, 1 = HTML # (optional) Liste der anzuzeigenden Werte (ReadingName[:Comment:[Postfix[:FormatString]]],...) #------------------------------------------------------------------------------ sub SYSMON_ShowValuesFmt ($$$;@) { my ($name, $title, $format, @data) = @_; if($format != 0 && $format != 1) { return "unknown output format\r\n"; } my $hash = $main::defs{$name}; # nur, wenn es sich um eine SYSMON Instanz handelt if($hash->{TYPE} eq 'SYSMON') { SYSMON_updateCurrentReadingsMap($hash); } #Log 3, "SYSMON $>name, @data<"; my @dataDescription = @data; if(scalar(@data)<=0) { # Array mit anzuzeigenden Parametern (Prefix, Name (in Map), Postfix) my $deg = "°"; if($format == 1) { $deg = "°"; } # bei der Benutzung mit CloneDummies ist $cur_readings_map nicht unbedingt definiert @dataDescription = (DATE, #CPU_TEMP.":".$hash->{helper}{cur_readings_map}->{+CPU_TEMP}.": ".$deg."C", CPU_TEMP.":"."CPU temperature".": ".$deg."C".":%.1f", #CPU_FREQ.":".$hash->{helper}{cur_readings_map}->{+CPU_FREQ}.": "."MHz", CPU_FREQ.":"."CPU frequency".": "."MHz", CPU_MODEL_NAME, CPU_BOGOMIPS, UPTIME_TEXT, FHEMUPTIME_TEXT, LOADAVG, RAM, SWAP, "power_ac_text", "power_usb_text", "power_battery_text"); # network-interfaces my $networks = AttrVal($name, "network-interfaces", undef); if(defined $networks) { my @networks_list = split(/,\s*/, trim($networks)); foreach (@networks_list) { my($nName, $nDef, $nComment) = split(/:/, $_); push(@dataDescription, $nName); } } # named filesystems my $filesystems = AttrVal($name, "filesystems", undef); if(defined $filesystems) { my @filesystem_list = split(/,\s*/, trim($filesystems)); foreach (@filesystem_list) { my($fName, $fDef, $fComment) = split(/:/, $_); push(@dataDescription, $fName); } } # User defined my $userdefined = AttrVal($name, "user-defined", undef); if(defined $userdefined) { my @userdefined_list = split(/,\s*/, trim($userdefined)); foreach (@userdefined_list) { # ::: my($uName, $uInterval, $uComment, $uCmd) = split(/:/, $_); push(@dataDescription, $uName); } } } #TODO: UserDefinedFn? my $map; if($hash->{TYPE} eq 'SYSMON') { $map = SYSMON_obtainParameters($hash, 1); } else { # Wenn nicht SYSMON, dann versuchen, die Daten aus den Readings auszulesen #$map = SYSMON_obtainReadings($hash); foreach my $rname (keys %{$hash->{READINGS}}) { my $rval=$hash->{READINGS}->{$rname}->{VAL}; $map->{$rname}=$rval; } } my $div_class="sysmon"; my $htmlcode; if($format == 1) { $htmlcode = "
"; } else { if($format == 0) { $htmlcode = ""; } } if(defined $title) { if($format == 1) { $htmlcode .= ""; } else { if($format == 0) { $htmlcode .= sprintf("%s\r\n", $title); } } } # oben definierte Werte anzeigen foreach (@dataDescription) { my($rName, $rComment, $rPostfix, $fmtStr) = split(/:/, $_); if(defined $rName) { if(!defined $rComment) { $rComment = $hash->{helper}{cur_readings_map}->{$rName}; } my $rVal = $map->{$rName}; if(!defined $rVal) { # ggf. userReadings verarbeiten $rVal = ReadingsVal($name,$rName,undef); } if($rName eq DATE) { # Datum anzeigen $rVal = strftime("%d.%m.%Y %H:%M:%S", localtime()); } if(!defined $rPostfix) { $rPostfix = ""; } if(defined $rVal) { if(defined($fmtStr)) { $rVal = sprintf($fmtStr,$rVal); } if($format == 1) { $htmlcode .= ""; } else { if($format == 0) { $htmlcode .= sprintf("%-24s: %s%s\r\n", $rComment, $rVal,$rPostfix); } } } } } # nur Default (also alles anzeigen) if(scalar(@data)<=0) { # File systems foreach my $aName (sort keys %{$map}) { if(defined ($aName) && index($aName, FS_PREFIX) == 0) { $aName =~ /^~ (.+)/; if($format == 1) { $htmlcode .= ""; } else { if($format == 0) { $htmlcode .= sprintf("%-24s: %s\r\n", "File System: ".$1,$map->{$aName}); } } } } } if($format == 1) { $htmlcode .= "
".$title."
".$rComment.": ".$rVal.$rPostfix."
File System: ".$1." ".$map->{$aName}."

"; } else { if($format == 0) { $htmlcode .= ""; } } return $htmlcode; } #sub SYSMON_first($) { # my (@d) = @_; # return @d[0]; # # #my ($d) = @_; # # # #return undef unless defined $d; # # ##return ref ($d)." - ".ref(\$d); # #if (ref $d eq "ARRAY") { # # return @{$d}[0]; # #} else { # # return $d; # #} #} # #sub SYSMON_last($) { # my (@d) = @_; # # return undef unless defined @d; # # return @d[-1]; # # #return ref ($d)." - ".ref(\$d); # #if (ref $d eq "ARRAY") { # # return @{$d}[-1]; # #} else { # # return $d; # #} #} #my $proc_fs = undef; sub SYSMON_isProcFS($) { my ($hash) = @_; if(!defined $hash->{helper}{proc_fs}) { $hash->{helper}{proc_fs} = int(SYSMON_execute($hash, "[ -d /proc/ ] && echo 1 || echo 0")); } return $hash->{helper}{proc_fs}; } #my $sys_cpu_temp_rpi = undef; sub SYSMON_isCPUTempRPi($) { my ($hash) = @_; if(!defined $hash->{helper}{sys_cpu_temp_rpi}) { $hash->{helper}{sys_cpu_temp_rpi} = int(SYSMON_execute($hash, "[ -f /sys/class/thermal/thermal_zone0/temp ] && echo 1 || echo 0")); } return $hash->{helper}{sys_cpu_temp_rpi}; } #my $sys_cpu_temp_bbb = undef; sub SYSMON_isCPUTempBBB($) { my ($hash) = @_; if(!defined $hash->{helper}{sys_cpu_temp_bbb}) { $hash->{helper}{sys_cpu_temp_bbb} = int(SYSMON_execute($hash, "[ -f /sys/class/hwmon/hwmon0/device/temp1_input ] && echo 1 || echo 0")); } return $hash->{helper}{sys_cpu_temp_bbb}; } #my $sys_cpu_freq_rpi_bbb = undef; sub SYSMON_isCPUFreqRPiBBB($) { my ($hash) = @_; if(!defined $hash->{helper}{sys_cpu_freq_rpi_bbb}) { #$hash->{helper}{sys_cpu_freq_rpi_bbb} = int(SYSMON_execute($hash, "[ -f /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq ] && echo 1 || echo 0")); # Diese abenteuerliche Konstruktion ist noetig, weil bei zu langen Zeilen ueber Telnet der Rest der Zeile als erstes Element kommt my @t = SYSMON_execute($hash, "[ -f /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq ] && echo 1 || echo 0"); if(@t) { $hash->{helper}{sys_cpu_freq_rpi_bbb} = int($t[-1]); } } return $hash->{helper}{sys_cpu_freq_rpi_bbb}; } # DUMMY sub SYSMON_isCPUTempFB($) { my ($hash) = @_; return SYSMON_isFB($hash); } sub SYSMON_isCPUTemp_X($$) { my ($hash, $cpuNum) = @_; if(!defined $hash->{helper}{"sys_cpu".$cpuNum."_temp"}) { #/sys/class/hwmon/hwmon0/device/hwmon/hwmon0/temp2_input #$hash->{helper}{"sys_cpu".$cpuNum."_temp"} = int(SYSMON_execute($hash, "[ -f /sys/class/hwmon/hwmon0/device/hwmon/hwmon0/temp".$cpuNum."_input ] && echo 1 || echo 0")); # s. o. my @t = SYSMON_execute($hash, "[ -f /sys/class/hwmon/hwmon0/device/hwmon/hwmon0/temp".($cpuNum+1)."_input ] && echo 1 || echo 0"); if(@t) { $hash->{helper}{"sys_cpu".$cpuNum."_temp"} = int($t[-1]); } } return $hash->{helper}{"sys_cpu".$cpuNum."_temp"}; } sub SYSMON_isCPUXFreq($$) { my ($hash, $cpuNum) = @_; if(!defined $hash->{helper}{"sys_cpu".$cpuNum."_freq"}) { #$hash->{helper}{"sys_cpu".$cpuNum."_freq"} = int(SYSMON_execute($hash, "[ -f /sys/devices/system/cpu/cpu".$cpuNum."/cpufreq/scaling_cur_freq ] && echo 1 || echo 0")); # s. o. my @t = SYSMON_execute($hash, "[ -f /sys/devices/system/cpu/cpu".$cpuNum."/cpufreq/scaling_cur_freq ] && echo 1 || echo 0"); if(@t) { $hash->{helper}{"sys_cpu".$cpuNum."_freq"} = int($t[-1]); } } return $hash->{helper}{"sys_cpu".$cpuNum."_freq"}; } #my $sys_fb = undef; sub SYSMON_isFB($) { my ($hash) = @_; if(!defined ($hash->{helper}{sys_fb})) { $hash->{helper}{sys_fb} = int(SYSMON_execute($hash, "[ -f /usr/bin/ctlmgr_ctl ] && echo 1 || echo 0")); } return $hash->{helper}{sys_fb}; } #-Power------- #my $sys_power_ac = undef; sub SYSMON_isSysPowerAc($) { my ($hash) = @_; if(!defined $hash->{helper}{sys_power_ac}) { $hash->{helper}{sys_power_ac} = int(SYSMON_execute($hash, "[ -f /sys/class/power_supply/ac/online ] && echo 1 || echo 0")); } return $hash->{helper}{sys_power_ac}; } #my $sys_power_usb = undef; sub SYSMON_isSysPowerUsb($) { my ($hash) = @_; if(!defined $hash->{helper}{sys_power_usb}) { $hash->{helper}{sys_power_usb} = int(SYSMON_execute($hash, "[ -f /sys/class/power_supply/usb/online ] && echo 1 || echo 0")); } return $hash->{helper}{sys_power_usb}; } #my $sys_power_bat = undef; sub SYSMON_isSysPowerBat($) { my ($hash) = @_; if(!defined $hash->{helper}{sys_power_bat}) { $hash->{helper}{sys_power_bat} = int(SYSMON_execute($hash, "[ -f /sys/class/power_supply/battery/online ] && echo 1 || echo 0")); } return $hash->{helper}{sys_power_bat}; } #my $sys_cpu_num = undef; sub SYSMON_isSysCpuNum($) { my ($hash) = @_; if(!defined $hash->{helper}{sys_cpu_num}) { $hash->{helper}{sys_cpu_num} = int(SYSMON_execute($hash, "[ -f /sys/devices/system/cpu/kernel_max ] && echo 1 || echo 0")); } return $hash->{helper}{sys_cpu_num}; } sub SYSMON_isNetStatClass($$) { my ($hash, $nName) = @_; if(!defined $hash->{helper}{'net_'.$nName.'_stat_class'}) { $hash->{helper}{'net_'.$nName.'_stat_class'} = int(SYSMON_execute($hash, "[ -f /sys/class/net/$nName/statistics/rx_bytes ] && echo 1 || echo 0")); # /sys/class/net/$nName/statistics/tx_bytes } return $hash->{helper}{'net_'.$nName.'_stat_class'}; } sub SYSMON_PowerAcInfo($$) { #online, present, current_now (/1000 =>mA), voltage_now (/1000000 => V) my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'powerinfo'}) {return $map;} my $type="ac"; my $base = "cat /sys/class/power_supply/".$type."/"; my $d_online_t = SYSMON_execute($hash, $base."online"); if($d_online_t) { my $d_online = trim($d_online_t); my $d_present = trim(SYSMON_execute($hash, $base."present 2>/dev/null")); my $d_current = SYSMON_execute($hash, $base."current_now 2>/dev/null"); if(defined $d_current) {$d_current/=1000;} else {return $map;} my $d_voltage = SYSMON_execute($hash, $base."voltage_now 2>/dev/null"); if(defined $d_voltage) {$d_voltage/=1000000;} else {return $map;} #$map->{"power_".$type."_online"}=$d_online; #$map->{"power_".$type."_present"}=$d_present; #$map->{"power_".$type."_current"}=$d_current; #$map->{"power_".$type."_voltage"}=$d_voltage; $map->{"power_".$type."_stat"}="$d_online $d_present $d_voltage $d_current"; $map->{"power_".$type."_text"}=$type.": ".(($d_present eq "1") ? "present" : "absent")." / ".($d_online eq "1" ? "online" : "offline").", voltage: ".$d_voltage." V, current: ".$d_current." mA, ".(int(($d_voltage*$d_current/100+0.5))/10)." W"; } return $map; } sub SYSMON_PowerUsbInfo($$) { #online, present, current_now (/1000 =>mA), voltage_now (/1000000 => V) my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'powerinfo'}) {return $map;} my $type="usb"; my $base = "cat /sys/class/power_supply/".$type."/"; my $d_online = trim(SYSMON_execute($hash, $base."online")); my $d_present = trim(SYSMON_execute($hash, $base."present 2>/dev/null")); my $d_current = SYSMON_execute($hash, $base."current_now 2>/dev/null"); if(defined $d_current) {$d_current/=1000;} else {return $map;} my $d_voltage = SYSMON_execute($hash, $base."voltage_now 2>/dev/null"); if(defined $d_voltage) {$d_voltage/=1000000;} else {return $map;} #$map->{"power_".$type."_online"}=$d_online; #$map->{"power_".$type."_present"}=$d_present; #$map->{"power_".$type."_current"}=$d_current; #$map->{"power_".$type."_voltage"}=$d_voltage; $map->{"power_".$type."_stat"}="$d_online $d_present $d_voltage $d_current"; $map->{"power_".$type."_text"}=$type.": ".(($d_present eq "1") ? "present" : "absent")." / ".($d_online eq "1" ? "online" : "offline").", voltage: ".$d_voltage." V, current: ".$d_current." mA, ".(int(($d_voltage*$d_current/100+0.5))/10)." W"; return $map; } sub SYSMON_PowerBatInfo($$) { #online, present, current_now (/1000 =>mA), voltage_now (/1000000 => V) my ($hash, $map) = @_; if($hash->{helper}->{excludes}{'powerinfo'}) {return $map;} my $type="battery"; my $base = "cat /sys/class/power_supply/".$type."/"; my $d_online = trim(SYSMON_execute($hash, $base."online")); my $d_present = trim(SYSMON_execute($hash, $base."present 2>/dev/null")); my $d_current = SYSMON_execute($hash, $base."current_now 2>/dev/null"); if(defined $d_current) {$d_current/=1000;} else {return $map;} my $d_voltage = SYSMON_execute($hash, $base."voltage_now 2>/dev/null"); if(defined $d_voltage) {$d_voltage/=1000000;} else {return $map;} my $d_capacity = trim(SYSMON_execute($hash, $base."capacity 2>/dev/null")); if($d_present ne "1") { $d_capacity = "0"; } #$map->{"power_".$type."_online"}=$d_online; #$map->{"power_".$type."_present"}=$d_present; #$map->{"power_".$type."_current"}=$d_current; #$map->{"power_".$type."_voltage"}=$d_voltage; $map->{"power_".$type."_stat"}="$d_online $d_present $d_voltage $d_current $d_capacity"; $map->{"power_".$type."_text"}=$type.": ".(($d_present eq "1") ? "present" : "absent")." / ".($d_online eq "1" ? "online" : "offline").", voltage: ".$d_voltage." V, current: ".$d_current." mA, ".(int(($d_voltage*$d_current/100+0.5))/10)." W, "."capacity: ".$d_capacity." %"; if($d_present eq "1") { # Zusaetzlich: technology, capacity, status, health, temp (/10 => °C) my $d_technology = trim(SYSMON_execute($hash, $base."technology 2>/dev/null")); my $d_status = trim(SYSMON_execute($hash, $base."status 2>/dev/null")); my $d_health = trim(SYSMON_execute($hash, $base."health 2>/dev/null")); my $d_energy_full_design = trim(SYSMON_execute($hash, $base."energy_full_design 2>/dev/null")); $map->{"power_".$type."_info"}=$type." info: ".$d_technology." , capacity: ".$d_capacity." %, status: ".$d_status." , health: ".$d_health." , total capacity: ".$d_energy_full_design." mAh"; # ggf. noch irgendwann: model_name, voltage_max_design, voltage_min_design } else { $map->{"power_".$type."_info"}=$type." info: n/a , capacity: n/a %, status: n/a , health: n/a , total capacity: n/a mAh"; } return $map; } #------------- sub SYSMON_execute($$) { my ($hash, $cmd) = @_; return SYSMON_Exec($hash, $cmd); #return qx($cmd); } #------------------------------------------------------------------------------ # checks and stores password used for remote connection sub SYSMON_storePassword($$) { my ($hash, $password) = @_; my $index = $hash->{TYPE}."_".$hash->{NAME}."_passwd"; my $key = getUniqueId().$index; my $enc_pwd = ""; if(eval "use Digest::MD5;1") { $key = Digest::MD5::md5_hex(unpack "H*", $key); $key .= Digest::MD5::md5_hex($key); } for my $char (split //, $password) { my $encode=chop($key); $enc_pwd.=sprintf("%.2x",ord($char)^ord($encode)); $key=$encode.$key; } my $err = setKeyValue($index, $enc_pwd); $hash->{helper}{error_msg}=$err; return "error while saving the password - $err" if(defined($err)); return "password successfully saved"; } # read password sub SYSMON_readPassword($) { my ($hash) = @_; my $name = $hash->{NAME}; my $index = $hash->{TYPE}."_".$hash->{NAME}."_passwd"; my $key = getUniqueId().$index; my ($password, $err); SYSMON_Log($hash, 5, "Read password from file"); ($err, $password) = getKeyValue($index); if(defined($err)) { SYSMON_Log($hash, 3, "unable to read password from file: $err"); return undef; } if(defined($password)) { if(eval "use Digest::MD5;1") { $key = Digest::MD5::md5_hex(unpack "H*", $key); $key .= Digest::MD5::md5_hex($key); } my $dec_pwd = ''; for my $char (map { pack('C', hex($_)) } ($password =~ /(..)/g)) { my $decode=chop($key); $dec_pwd.=chr(ord($char)^ord($decode)); $key=$decode.$key; } return $dec_pwd; } else { SYSMON_Log($hash, 5, "No password in file"); return undef; } } # Opens a Telnet Connection to an external Machine ############################################ sub SYSMON_Open_Connection($) { my ($hash) = @_; my $name = $hash->{NAME}; my $msg; my $mode = $hash->{MODE};#AttrVal( $name, 'mode', 'local'); if ($mode eq 'local') { return undef; } if($missingModulRemote) { $msg="Error: Perl modul ".$missingModulRemote."is missing on this system. Please install before using this modul."; SYSMON_Log($hash, 3, $msg); return $msg; } my $host = $hash->{HOST};#AttrVal( $name, "remote_host", undef ); if(!defined $host) { $msg="Error: no remote host provided"; SYSMON_Log($hash, 3, $msg); return $msg unless defined $host; } my $port = $hash->{PORT};#AttrVal( $name, "remote_port", 23 ); my $pwd = SYSMON_readPassword($hash);#AttrVal( $name, "remote_password", undef ); my $user = $hash->{USER};#AttrVal( $name, "remote_user", "" ); $user="" unless defined($user); #test #$pwd="dummy"; #test my $before; my $match; #if(!defined($pwd)) { # my $pwdFile = AttrVal( $name, "pwdFile", undef); # if(defined($pwdFile)) { # SYSMON_Log($hash, 5, "Open password file '$pwdFile' to extract password"); # if (open(IN, "<" . $pwdFile)) { # $pwd = ; # close(IN); # SYSMON_Log($hash, 5, "Close password file"); # } else { # $msg = "Error: Cannot open password file '$pwdFile': $!"; # SYSMON_Log($hash, 2, $msg); # return $msg; # } # } #} if(!defined($pwd)) { $msg="Error: no password provided"; SYSMON_Log($hash, 3, $msg); return $msg unless defined $pwd; } SYSMON_Log($hash, 5, "Open Telnet connection to $host:$port"); my $timeout = AttrVal( $name, "telnet-time-out", "10"); my $t_prompt=AttrVal($name,'telnet-prompt-regx','(#|\$)\s*$'); #my $telnet = new Net::Telnet ( Host=>$host, Port => $port, Timeout=>$timeout, Errmode=>'return', Prompt=>'/(#|\$) $/'); my $telnet = new Net::Telnet ( Host=>$host, Port => $port, Timeout=>$timeout, Errmode=>'return', Prompt=>'/'.$t_prompt.'/'); if (!$telnet) { $msg = "Could not open telnet connection to $host:$port"; SYSMON_Log($hash, 2, $msg); $telnet = undef; $hash->{".telnet"}=$telnet; return $msg; } $hash->{".telnet"}=$telnet; SYSMON_Log($hash, 5, "Wait for user or password prompt."); unless ( ($before,$match) = $telnet->waitfor('/(user|login|password): $/i') ) { $msg = "Telnet error while waiting for user or password prompt: ".$telnet->errmsg; SYSMON_Log($hash, 2, $msg); $telnet->close; $telnet = undef; return $msg; } if ( $match =~ /(user|login): / && $user eq "") { $msg = "Telnet login requires user name but attribute 'telnetUser' not defined"; SYSMON_Log($hash, 2, $msg); $telnet->close; $telnet = undef; return $msg; } elsif ( $match =~ /(user|login): /) { SYSMON_Log($hash, 5, "Entering user name"); $telnet->print( $user ); SYSMON_Log($hash, 5, "Wait for password prompt"); unless ($telnet->waitfor( '/password: $/i' )) { $msg = "Telnet error while waiting for password prompt: ".$telnet->errmsg; SYSMON_Log($hash, 2, $msg); $telnet->close; $telnet = undef; return $msg; } } elsif ( $match eq "password: " && $user ne "") { SYSMON_Log($hash, 3, "remote user was defined but telnet login did not prompt for user name."); } SYSMON_Log($hash, 5, "Entering password"); $telnet->print( $pwd ); SYSMON_Log($hash, 5, "Wait for command prompt"); my $tlogin_prompt=AttrVal($name,'telnet-login-prompt-regx','(#|\$|>)\s*$|Login failed.'); #unless ( ($before,$match) = $telnet->waitfor( '/# $|Login failed./i' )) unless ( ($before,$match) = $telnet->waitfor( '/'.$tlogin_prompt.'/i' )) { $msg = "Telnet error while waiting for command prompt: ".$telnet->errmsg; SYSMON_Log($hash, 2, $msg); $telnet->close; $telnet = undef; return $msg; } elsif ( $match eq "Login failed.") { $msg = "Telnet error: Login failed. Wrong password."; SYSMON_Log($hash, 2, $msg); $telnet->close; $telnet = undef; return $msg; } #SYSMON_Log($hash, 2, "Prompt: ".Dumper($before)." > ".$match); # Promptzeile erkenen if(!($hash->{helper}{recognized_prompt})) { my @prompt = SYSMON_Exec_Remote($hash, ''); if(scalar(@prompt) == 1) { $hash->{helper}{recognized_prompt}=$prompt[0]; } } #SYSMON_Log($hash, 2, "Prompt: '".Dumper(@retVal)."'"); return undef; } # end SYSMON_Open_Connection # Closes a Telnet Connection to an external Machine ############################################ sub SYSMON_Close_Connection($) { my ($hash) = @_; my $name = $hash->{NAME}; my $mode = $hash->{MODE};#AttrVal( $name, 'mode', 'local'); if (!defined($mode) || $mode eq 'local') { return undef; } my $telnet = $hash->{".telnet"}; if (defined $telnet) { SYSMON_Log ($hash, 5, "Close Telnet connection"); $telnet->close; $telnet = undef; $hash->{".telnet"}=$telnet; } else { SYSMON_Log($hash, 1, "Cannot close an undefined Telnet connection"); } } # end SYSMON_Close_Connection # Executed the command on the remote Shell ############################################ sub SYSMON_Exec($$;$) { my ($hash, $cmd,$is_arr) = @_; my $openedTelnet = 0; my $telnet = $hash->{".telnet"}; my $name = $hash->{NAME}; my $mode = $hash->{MODE};#AttrVal( $name, 'mode', 'local'); if ($mode eq 'telnet') { unless (defined $telnet) { SYSMON_Log($hash, 5, "$name: Open single telnet connection"); my $msg = SYSMON_Open_Connection($hash); $hash->{helper}{error_msg}=$msg; if ($msg) { return undef; } $openedTelnet = 1; $hash->{helper}{error_msg}=undef; } my @retVal = SYSMON_Exec_Remote($hash, $cmd); if($openedTelnet) { SYSMON_Log($hash, 5, "$name: Close single telnet connection"); SYSMON_Close_Connection( $hash ); } #Prompt-Zeile entfernen, falls vorhanden my $recognized_prompt = $hash->{helper}{recognized_prompt}; if(defined($recognized_prompt)) { if(scalar(@retVal)>=1) { if($retVal[-1] eq $recognized_prompt) { SYSMON_Log ($hash, 5, "remove prompt: ".$retVal[-1]."'"); splice @retVal, -1, 1;# $retVal[-1]; } } } # Arrays als solche zurueckgeben #if($is_arr && scalar(@retVal)>1) { if(scalar(@retVal)>1) { SYSMON_Log ($hash, 5, "Result A: '".Dumper(@retVal)."'"); return @retVal; } # Einzeiler als normale Scalars my $line = $retVal[0]; if(defined($line)) { chomp $line; SYSMON_Log ($hash, 5, "Result L: '$line'"); } else { SYSMON_Log ($hash, 5, "Result undef"); } return $line; #return $retVal; } else { if ($mode eq 'ssh') { return SYSMON_Exec_Ssh($hash, $cmd); } else { return SYSMON_Exec_Local($hash, $cmd); } } } sub MYTEST() { my @output=( '', '[~] ', '', '[~] # ', '', '', '', ' Interrupt:16 Memory:c0100000-c0120000 ', '', ' RX bytes:483322579219 (450.1 GiB) TX bytes:3757348645531 (3.4 TiB)', '', ' collisions:0 txqueuelen:1000 ', '', ' TX packets:3656315540 errors:0 dropped:0 overruns:0 carrier:0', '', ' RX packets:2817622543 errors:8 dropped:265294 overruns:0 frame:8', '', ' UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1', '', ' inet addr:192.168.178.80 Bcast:192.168.178.255 Mask:255.255.255.0', '', 'eth0 Link encap:Ethernet HWaddr 00:08:9B:D3:8D:9E' ); @output = reverse(@output); for (my $i=0;$i{".telnet"}; SYSMON_Log($hash, 5, "Execute '".$cmd."'"); @output=$telnet->cmd($cmd); #SYSMON_Log($hash, 5, "Result '".Dumper(@output)."'"); # TODO: remove # Sonderlocke fuer QNAP: letzten Zeilen mit "[~] " am Anfang entfernen #while((scalar(@output)>0) && ($output[-1]=~ /^\[~\]/)) { # SYSMON_Log ($hash, 5, "Remove line: '".$output[-1]."'"); # splice @output, -1, 1; #} for (my $i=0;$i>> Line >>> '".$output[$i]."'"); # TODO: remove if($output[$i]=~ /^\[~\]/) {undef ($output[$i]);} } #SYSMON_Log($hash, 5, "Result >>> vgrep >>>'".Dumper(@output)."'"); # TODO: remove @output = grep{ defined($_) && trim($_) ne '' }@output; #SYSMON_Log($hash, 5, "Result >>> ngrep >>>'".Dumper(@output)."'"); # TODO: remove return @output; ## Arrays als solche zurueckgeben #if(scalar(@output)>1) { # SYSMON_Log ($hash, 5, "Result '".Dumper(@output)."'"); # return @output; #} ## Einzeiler als normale Scalars #my $line = @output[0]; #chomp $line; #SYSMON_Log ($hash, 5, "Result '$line'"); #return $line; #$result = $output[0]; ##chomp $result; #my $log = join " ", @output; #chomp $log; #SYSMON_Log($hash, 5, "Result '$log'"); #return $result; } # Executed the command on the remote Shell sub ############################################ SYSMON_Exec_Local($$) { my ($hash, $cmd) = @_; SYSMON_Log($hash, 5, "Execute '".$cmd."'"); #return qx($cmd); my @result = qx($cmd); # Arrays als solche zurueckgeben if(scalar(@result)>1) { SYSMON_Log ($hash, 5, "Result '".Dumper(@result)."'"); return @result; } # Einzeiler als normale Scalars my $line = $result[0]; if(defined($line)) { chomp $line; SYSMON_Log ($hash, 5, "Result '$line'"); } else { SYSMON_Log ($hash, 5, "Result undef"); } return $line; #chomp $result; #SYSMON_Log ($hash, 5, "Result '$result'"); #return $result; } # Executed the command on the remote SSH Shell sub ############################################ SYSMON_Exec_Ssh($$) { my ($hash, $cmd) = @_; my $msg; my $name = $hash->{NAME}; my $host = $hash->{HOST};#AttrVal( $name, "remote_host", undef ); if(!defined $host) { $msg="Error: no remote host provided"; SYSMON_Log($hash, 3, $msg); return $msg unless defined $host; } my $pwd = SYSMON_readPassword($hash);#AttrVal( $name, "remote_password", undef ); my $t_sshpass = ''; if(defined($pwd)) { #$msg="Error: no passwort provided"; #SYSMON_Log($hash, 3, $msg); #return $msg unless defined $pwd; $t_sshpass = 'echo '.$pwd.' | sshpass '; #$t_sshpass = 'sshpass -p '.$pwd.' '; } my $user = $hash->{USER};#AttrVal( $name, "remote_user", "" ); my $port = $hash->{PORT};#AttrVal( $name, "remote_port", "22" ); SYSMON_Log($hash, 5, "Execute '".$cmd."' by SSH"); my $p_tmp = ''; my $sshParam = AttrVal($name,"ssh-params",undef); if(defined($sshParam)) { $p_tmp = $p_tmp.' '.$sshParam.' '; } if(defined($port)) { $p_tmp = $p_tmp.' -p '.$port.' '; } my $call = "ssh ".$p_tmp.$user."\@".$host." ".'"'.$cmd.'"'; SYSMON_Log ($hash, 5, "Call: '".$call."'"); $call = $t_sshpass.$call; # $call = $call.' 2>/dev/null'; my @result = qx($call); # Arrays als solche zurueckgeben if(scalar(@result)>1) { SYSMON_Log ($hash, 5, "Result '".Dumper(@result)."'"); return @result; } # Einzeiler als normale Scalars my $line = $result[0]; if(defined($line)) { chomp $line; SYSMON_Log ($hash, 5, "Result '$line'"); } else { SYSMON_Log ($hash, 5, "Result undef"); } return $line; #chomp $result; #SYSMON_Log ($hash, 5, "Result '$result'"); #return $result; } #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Uebersetzt Sekunden (Dauer) in Tage/Stunden/Minuten/Sekunden #------------------------------------------------------------------------------ sub SYSMON_decode_time_diff($) { my $s = shift; my $d = int($s/86400); $s -= $d*86400; my $h = int($s/3600); $s -= $h*3600; my $m = int($s/60); #$s -= $m*60; #return ($d,$h,$m,$s); return ($d,$h,$m); } #------------------------------------------------------------------------------ # Logging: Funkrionsaufrufe # Parameter: HASH, Funktionsname, Message #------------------------------------------------------------------------------ #sub logF($$$) #{ # my ($hash, $fname, $msg) = @_; # #Log 5, "SYSMON $fname (".$hash->{NAME}."): $msg"; # Log 5, "SYSMON $fname $msg"; #} sub SYSMON_Log($$$) { my ( $hash, $loglevel, $text ) = @_; my $xline = ( caller(0) )[2]; my $xsubroutine = ( caller(1) )[3]; my $sub = ( split( ':', $xsubroutine ) )[2]; $sub =~ s/SYSMON_//; my $instName = ( ref($hash) eq "HASH" ) ? $hash->{NAME} : $hash; $instName="" unless $instName; Log3 $hash, $loglevel, "SYSMON $instName: $sub.$xline " . $text; } # ----------------------------------------------------------------------------- 1; =pod =item device =item summary provides some statistics about the system =item summary_DE liefert einige Statistiken ueber das Host-System =begin html

SYSMON

(en | de)
    This module provides statistics about the system running FHEM server. Furthermore, remote systems can be accessed (Telnet). Only Linux-based systems are supported. Some informations are hardware specific and are not available on every platform. So far, this module has been tested on the following systems: Raspberry Pi (Debian Wheezy), BeagleBone Black, FritzBox 7390, WR703N under OpenWrt, CubieTruck and some others.

    For more information on a FritzBox check other moduls: FRITZBOX and FB_CALLMONITOR.
    The modul uses the Perl modul 'Net::Telnet' for remote access. Please make sure that this module is installed.

    Define

    define <name> SYSMON [MODE[:[USER@]HOST][:PORT]] [<M1>[ <M2>[ <M3>[ <M4>]]]]

    This statement creates a new SYSMON instance. The parameters M1 to M4 define the refresh interval for various Readings (statistics). The parameters are to be understood as multipliers for the time defined by INTERVAL_BASE. Because this time is fixed at 60 seconds, the Mx-parameter can be considered as time intervals in minutes.
    If one (or more) of the multiplier is set to zero, the corresponding readings is deactivated.

    The parameters are responsible for updating the readings according to the following scheme:
    • M1: (Default: 1)
      cpu_freq, cpu_temp, cpu_temp_avg, loadavg, stat_cpu, stat_cpu_diff, stat_cpu_percent, stat_cpu_text, power readings

    • M2: (Default: M1)
      ram, swap
    • M3: (Default: M1)
      eth0, eth0_diff, wlan0, wlan0_diff

    • M4: (Default: 10*M1)
      Filesystem informations

    • The following parameters are always updated with the base interval (regardless of the Mx-parameter):
      fhemuptime, fhemuptime_text, idletime, idletime_text, uptime, uptime_text, starttime, starttime_text

    To query a remote system at least the address (HOST) must be specified. Accompanied by the port and / or user name, if necessary. The password (if needed) has to be defined once with the command 'set password <password>'. For MODE parameter are 'telnet', 'ssh' and 'local' only allowed. 'local' does not require any other parameters and can also be omitted.
    For SSH login with password, 'sshpass' must be installed (note: not recommended! Use public key authentication instead). For SSH login to work, a manual SSH connection to the remote machine from the FHEM-Acount may need to be done once (under whose rights FHEM runs) the fingerprint must be confirmed.

    Readings:

    • cpu_core_count
      CPU core count
    • cpu_model_name
      CPU model name
    • cpu_bogomips
      CPU Speed: BogoMIPS
    • cpu_freq (and cpu1_freq for dual core systems)
      CPU frequency

    • cpu_temp
      CPU temperature

    • cpu_temp_avg
      Average of the CPU temperature, formed over the last 4 values.

    • fhemuptime
      Time (in seconds) since the start of FHEM server.

    • fhemuptime_text
      Time since the start of the FHEM server: human-readable output (text representation).

    • fhemstarttime
      Start time (in seconds since 1.1.1970 1:00:00) of FHEM server.

    • fhemstarttime_text
      Start time of the FHEM server: human-readable output (text representation).

    • idletime
      Time spent by the system since the start in the idle mode (period of inactivity).

    • idletime_text
      The inactivity time of the system since system start in human readable form.

    • loadavg
      System load (load average): 1 minute, 5 minutes and 15 minutes.

    • ram
      memory usage.

    • swap
      swap usage.

    • uptime
      System uptime.

    • uptime_text
      System uptime (human readable).

    • starttime
      System starttime.

    • starttime_text
      System starttime (human readable).

    • Network statistics
      Statistics for the specified network interface about the data volumes transferred and the difference since the previous measurement.
      Examples:
      Amount of the transmitted data via interface eth0.
      eth0: RX: 940.58 MB, TX: 736.19 MB, Total: 1676.77 MB
      Change of the amount of the transferred data in relation to the previous call (for eth0).
      eth0_diff: RX: 0.66 MB, TX: 0.06 MB, Total: 0.72 MB
      IP and IP v6 adresses eth0_ip 192.168.0.15
      eth0_ip6 fe85::49:4ff:fe85:f885/64

    • Network Speed (if avialable)
      speed of the network connection.
      Examples:
      eth0_speed 100

    • File system information
      Usage of the desired file systems.
      Example:
      fs_root: Total: 7340 MB, Used: 3573 MB, 52 %, Available: 3425 MB at /

    • CPU utilization
      Information about the utilization of CPUs.
      Example:
      stat_cpu: 10145283 0 2187286 90586051 542691 69393 400342
      stat_cpu_diff: 2151 0 1239 2522 10 3 761
      stat_cpu_percent: 4.82 0.00 1.81 93.11 0.05 0.00 0.20
      stat_cpu_text: user: 32.17 %, nice: 0.00 %, sys: 18.53 %, idle: 37.72 %, io: 0.15 %, irq: 0.04 %, sirq: 11.38 %

    • user defined
      These readings provide output of commands, which are passed to the operating system or delivered by user defined functions.

    • FritzBox specific Readings
    • wlan_state
      WLAN state: on/off

    • wlan_guest_state
      GuestWLAN state: on/off

    • internet_ip
      current IP-Adresse

    • internet_state
      state of the Internet connection: connected/disconnected

    • night_time_ctrl
      state night time control (do not disturb): on/off

    • num_new_messages
      Number of new Voice Mail messages

    • fw_version_info
      Information on the installed firmware version: <VersionNum> <creation date> <time>

    • DSL Informations (FritzBox)
    • dsl_rate
      Information about the down und up stream rate

    • dsl_synctime
      sync time with DSLAM

    • dsl_crc_15
      number of uncorrectable errors (CRC) for the last 15 minutes

    • dsl_fec_15
      number of correctable errors (FEC) for the last 15 minutes

    • Power Supply Readings
    • power_ac_stat
      status information to the AC socket: online (0|1), present (0|1), voltage, current
      Example:
      power_ac_stat: 1 1 4.807 264

    • power_ac_text
      human readable status information to the AC socket
      Example:
      power_ac_text ac: present / online, voltage: 4.807 V, current: 264 mA

    • power_usb_stat
      status information to the USB socket

    • power_usb_text
      human readable status information to the USB socket

    • power_battery_stat
      status information to the battery (if installed): online (0|1), present (0|1), voltage, current, actual capacity
      Example:
      power_battery_stat: 1 1 4.807 264 100

    • power_battery_text
      human readable status information to the battery (if installed)

    • power_battery_info
      human readable additional information to the battery (if installed): technology, capacity, status, health, total capacity
      Example:
      power_battery_info: battery info: Li-Ion , capacity: 100 %, status: Full , health: Good , total capacity: 2100 mAh
      The capacity must be defined in script.bin (e.g. ct-hdmi.bin). Parameter name pmu_battery_cap. Convert with bin2fex (bin2fex -> script.fex -> edit -> fex2bin -> script.bin).

    • cpuX_freq_stat
      Frequency statistics for CPU X: minimum, maximum and average values
      Example:
      cpu0_freq_stat: 100 1000 900

    • cpuX_idle_stat
      Idle statistik for CPU X: minimum, maximum and average values
      Example:
      cpu0_freq_stat: 23.76 94.74 90.75

    • cpu[X]_temp_stat
      Temperature statistik for CPU: minimum, maximum and average values
      Example:
      cpu_temp_stat: 41.00 42.50 42.00

    • ram_used_stat
      RAM usage statistics: minimum, maximum and average values
      Example:
      ram_used_stat: 267.55 1267.75 855.00

    • swap_used_stat
      SWAP usage statistics: minimum, maximum and average values
      Example:
      swap_used_stat: 0 1024.00 250.00



    Get:

    • interval_base
      Lists the specified polling intervalls.

    • interval_multipliers
      Displays update intervals.

    • list
      Lists all readings.

    • update
      Refreshs all readings.

    • version
      Displays the version of SYSMON module.

    • list_lan_devices
      Displays known LAN Devices (FritzBox only).


    Set:

    • interval_multipliers
      Defines update intervals (as in the definition of the device).

    • clean
      Clears user-definable Readings. After an update (manual or automatic) new readings are generated.

    • clear <reading name>
      Deletes the Reading entry with the given name. After an update this entry is possibly re-created (if defined). This mechanism allows the selective deleting unnecessary custom entries.

    • password <Passwort>
      Specify the password for remote access (usually only necessary once).


    Attributes:

    • filesystems <reading name>[:<mountpoint>[:<comment>]],...
      Specifies the file system to be monitored (a comma-separated list).
      Reading-name is used in the display and logging, the mount point is the basis of the evaluation, comment is relevant to the HTML display (see SYSMON_ShowValuesHTML)
      Examples:
      /boot,/,/media/usb1
      fs_boot:/boot,fs_root:/:Root,fs_usb1:/media/usb1:USB-Stick

    • network-interfaces <name>[:<interface>[:<comment>]],...
      Comma-separated list of network interfaces that are to be monitored. Each entry consists of the Reading-name, the name of the Netwerk adapter and a comment for the HTML output (see SYSMON_ShowValuesHTML). If no colon is used, the value is used simultaneously as a Reading-name and interface name.
      Example ethernet:eth0:Ethernet,wlan:wlan0:WiFi

    • user-defined <readingsName>:<Interval_Minutes>:<Comment>:<Cmd>,...
      This comma-separated list defines user defined Readings with the following data: Reading name, refresh interval (in minutes), a Comment, and operating system command.
      The os commands are executed according to the specified Intervals and are noted as Readings with the specified name. Comments are used for the HTML output (see SYSMON_ShowValuesHTML)..
      All parameter parts are required!
      It is important that the specified commands are executed quickly, because at this time the entire FHEM server is blocked!
      If results of the long-running operations required, these should be set up as a CRON job and store results as a text file.

      Example: Display of package updates for the operating system:
      cron-Job:
      sudo apt-get update 2>/dev/null >/dev/null apt-get upgrade --dry-run| perl -ne '/(\d*)\s[upgraded|aktualisiert]\D*(\d*)\D*install|^ \S+.*/ and print "$1 aktualisierte, $2 neue Pakete"' 2>/dev/null > /opt/fhem/data/updatestatus.txt
      uder-defined attribute
      sys_updates:1440:System Aktualisierungen:cat /opt/fhem/data/updatestatus.txt
      the number of available updates is daily recorded as 'sys_updates'.

    • user-fn <fn_name>:<interval_minutes>:<reading_name1>:<reading_name2>...[:<reading_nameX>], ...
      List of perl user subroutines.
      As <fn_name> can be used either the name of a Perl subroutine or a Perl expression. The perl function gets the device hash as parameter and must provide an array of values. These values are taken according to the parameter <reading_nameX> in Readings.
      A Perl expression must be enclosed in curly braces and can use the following parameters: $ HASH (device hash) and $ NAME (device name). Return is expected analogous to a Perl subroutine.
      Important! The separation between multiple user functions must be done with a comma AND a space! Within the function definition commas may not be followed by spaces.

    • disable
      Possible values: 0 and 1. '1' means that the update is stopped.

    • telnet-prompt-regx, telnet-login-prompt-regx
      RegExp to detect login and command line prompt. (Only for access via Telnet.)

    • exclude
      Allows to suppress reading certain information.
      supported values: user-defined (s. user-defined und user-fn), cpucount, uptime, fhemuptime, loadavg, cputemp, cpufreq, cpuinfo, diskstat, cpustat, ramswap, filesystem, network, fbwlan, fbnightctrl, fbnewmessages, fbdecttemp, fbversion, fbdsl, powerinfo

    • ssh-param
      Adds additional parameters to the SSH call as specified.


    Plots:

      predefined gplot files:
        FileLog versions:
        SM_RAM.gplot
        SM_CPUTemp.gplot
        SM_FS_root.gplot
        SM_FS_usb1.gplot
        SM_Load.gplot
        SM_Network_eth0.gplot
        SM_Network_eth0t.gplot
        SM_Network_wlan0.gplot
        SM_CPUStat.gplot
        SM_CPUStatSum.gplot
        SM_CPUStatTotal.gplot
        SM_power_ac.gplot
        SM_power_usb.gplot
        SM_power_battery.gplot
        DbLog versions:
        SM_DB_all.gplot
        SM_DB_CPUFreq.gplot
        SM_DB_CPUTemp.gplot
        SM_DB_Load.gplot
        SM_DB_Network_eth0.gplot
        SM_DB_RAM.gplot

    HTML output method (see Weblink): SYSMON_ShowValuesHTML(<SYSMON-Instance>[,<Liste>])

      The module provides a function that returns selected Readings as HTML.
      As a parameter the name of the defined SYSMON device is expected.
      It can also Reading Group, Clone dummy or other modules be used. Their readings are simple used for display.
      The second parameter is optional and specifies a list of readings to be displayed in the format <ReadingName>[:<Comment>[:<Postfix>[:<FormatString>]]].
      ReadingName is the Name of desired Reading, Comment is used as the display name and postfix is displayed after the value (such as units or as MHz can be displayed). If FormatString is specified, the output is formatted with sprintf (s. sprintf in Perl documentation).
      If no Comment is specified, an internally predefined description is used.
      If no list specified, a predefined selection is used (all values are displayed).

      define sysv1 weblink htmlCode {SYSMON_ShowValuesHTML('sysmon')}
      define sysv2 weblink htmlCode {SYSMON_ShowValuesHTML('sysmon', ('date:Datum', 'cpu_temp:CPU Temperatur: °C:%.1f'', 'cpu_freq:CPU Frequenz: MHz'))}

    Text output method (see Weblink): SYSMON_ShowValuesHTMLTitled(<SYSMON-Instance>[,<Title>,<Liste>])

      According to SYSMON_ShowValuesHTML, but with a Title text above. If no title provided, device alias will be used (if any)

    Text output method (see Weblink): SYSMON_ShowValuesText(<SYSMON-Instance>[,<Liste>])

      According to SYSMON_ShowValuesHTML, but formatted as plain text.

    Text output method (see Weblink): SYSMON_ShowValuesTextTitled(<SYSMON-Instance>[,<Title>,<Liste>])

      According to SYSMON_ShowValuesHTMLTitled, but formatted as plain text.

    Reading values with perl: SYSMON_getValues(<name>[, <array of desired keys>])

      Returns a hash ref with desired values. If no array is passed, all values are returned.
      {(SYSMON_getValues("sysmon"))->{'cpu_temp'}}
      {(SYSMON_getValues("sysmon",("cpu_freq","cpu_temp")))->{"cpu_temp"}}
      {join(" ", values (SYSMON_getValues("sysmon")))}
      {join(" ", values (SYSMON_getValues("sysmon",("cpu_freq","cpu_temp"))))}

    Examples:

      # Modul-Definition
      define sysmon SYSMON 1 1 1 10
      #attr sysmon event-on-update-reading cpu_temp,cpu_temp_avg,cpu_freq,eth0_diff,loadavg,ram,^~ /.*usb.*,~ /$
      attr sysmon event-on-update-reading cpu_temp,cpu_temp_avg,cpu_freq,eth0_diff,loadavg,ram,fs_.*,stat_cpu_percent
      attr sysmon filesystems fs_boot:/boot,fs_root:/:Root,fs_usb1:/media/usb1:USB-Stick
      attr sysmon network-interfaces eth0:eth0:Ethernet,wlan0:wlan0:WiFi
      attr sysmon group RPi
      attr sysmon room 9.03_Tech

      # Log
      define FileLog_sysmon FileLog ./log/sysmon-%Y-%m.log sysmon
      attr FileLog_sysmon group RPi
      attr FileLog_sysmon logtype SM_CPUTemp:Plot,text
      attr FileLog_sysmon room 9.03_Tech

      # Visualisierung: CPU-Temperatur
      define wl_sysmon_temp SVG FileLog_sysmon:SM_CPUTemp:CURRENT
      attr wl_sysmon_temp group RPi
      attr wl_sysmon_temp label "CPU Temperatur: Min $data{min2}, Max $data{max2}, Last $data{currval2}"
      attr wl_sysmon_temp room 9.03_Tech

      # Visualisierung: Netzwerk-Datenübertragung für eth0
      define wl_sysmon_eth0 SVG FileLog_sysmon:SM_Network_eth0:CURRENT
      attr wl_sysmon_eth0 group RPi
      attr wl_sysmon_eth0 label "Netzwerk-Traffic eth0: $data{min1}, Max: $data{max1}, Aktuell: $data{currval1}"
      attr wl_sysmon_eth0 room 9.03_Tech

      # Visualisierung: Netzwerk-Datenübertragung für wlan0
      define wl_sysmon_wlan0 SVG FileLog_sysmon:SM_Network_wlan0:CURRENT
      attr wl_sysmon_wlan0 group RPi
      attr wl_sysmon_wlan0 label "Netzwerk-Traffic wlan0: $data{min1}, Max: $data{max1}, Aktuell: $data{currval1}"
      attr wl_sysmon_wlan0 room 9.03_Tech

      # Visualisierung: CPU-Auslastung (load average)
      define wl_sysmon_load SVG FileLog_sysmon:SM_Load:CURRENT
      attr wl_sysmon_load group RPi
      attr wl_sysmon_load label "Load Min: $data{min1}, Max: $data{max1}, Aktuell: $data{currval1}"
      attr wl_sysmon_load room 9.03_Tech

      # Visualisierung: RAM-Nutzung
      define wl_sysmon_ram SVG FileLog_sysmon:SM_RAM:CURRENT
      attr wl_sysmon_ram group RPi
      attr wl_sysmon_ram label "RAM-Nutzung Total: $data{max1}, Min: $data{min2}, Max: $data{max2}, Aktuell: $data{currval2}"
      attr wl_sysmon_ram room 9.03_Tech

      # Visualisierung: Dateisystem: Root-Partition
      define wl_sysmon_fs_root SVG FileLog_sysmon:SM_FS_root:CURRENT
      attr wl_sysmon_fs_root group RPi
      attr wl_sysmon_fs_root label "Root Partition Total: $data{max1}, Min: $data{min2}, Max: $data{max2}, Aktuell: $data{currval2}"
      attr wl_sysmon_fs_root room 9.03_Tech

      # Visualisierung: Dateisystem: USB-Stick
      define wl_sysmon_fs_usb1 SVG FileLog_sysmon:SM_FS_usb1:CURRENT
      attr wl_sysmon_fs_usb1 group RPi
      attr wl_sysmon_fs_usb1 label "USB1 Total: $data{max1}, Min: $data{min2}, Max: $data{max2}, Aktuell: $data{currval2}"
      attr wl_sysmon_fs_usb1 room 9.03_Tech

      # Anzeige der Readings zum Einbinden in ein 'Raum'.
      define SysValues weblink htmlCode {SYSMON_ShowValuesHTML('sysmon')}
      attr SysValues group RPi
      attr SysValues room 9.03_Tech

      # Anzeige CPU Auslasung
      define wl_sysmon_cpustat SVG FileLog_sysmon:SM_CPUStat:CURRENT
      attr wl_sysmon_cpustat label "CPU(min/max): user:$data{min1}/$data{max1} nice:$data{min2}/$data{max2} sys:$data{min3}/$data{max3} idle:$data{min4}/$data{max4} io:$data{min5}/$data{max5} irq:$data{min6}/$data{max6} sirq:$data{min7}/$data{max7}"
      attr wl_sysmon_cpustat group RPi
      attr wl_sysmon_cpustat room 9.99_Test
      attr wl_sysmon_cpustat plotsize 840,420
      define wl_sysmon_cpustat_s SVG FileLog_sysmon:SM_CPUStatSum:CURRENT
      attr wl_sysmon_cpustat_s label "CPU(min/max): user:$data{min1}/$data{max1} nice:$data{min2}/$data{max2} sys:$data{min3}/$data{max3} idle:$data{min4}/$data{max4} io:$data{min5}/$data{max5} irq:$data{min6}/$data{max6} sirq:$data{min7}/$data{max7}"
      attr wl_sysmon_cpustat_s group RPi
      attr wl_sysmon_cpustat_s room 9.99_Test
      attr wl_sysmon_cpustat_s plotsize 840,420
      define wl_sysmon_cpustatT SVG FileLog_sysmon:SM_CPUStatTotal:CURRENT
      attr wl_sysmon_cpustatT label "CPU-Auslastung"
      attr wl_sysmon_cpustatT group RPi
      attr wl_sysmon_cpustatT plotsize 840,420
      attr wl_sysmon_cpustatT room 9.99_Test

      # Anzeige Stromversorgung AC
      define wl_sysmon_power_ac SVG FileLog_sysmon:SM_power_ac:CURRENT
      attr wl_sysmon_power_ac label "Stromversorgung (ac) Spannung: $data{min1} - $data{max1} V, Strom: $data{min2} - $data{max2} mA"
      attr wl_sysmon_power_ac room Technik
      attr wl_sysmon_power_ac group system
      # Anzeige Stromversorgung Battery
      define wl_sysmon_power_bat SVG FileLog_sysmon:SM_power_battery:CURRENT
      attr wl_sysmon_power_bat label "Stromversorgung (bat) Spannung: $data{min1} - $data{max1} V, Strom: $data{min2} - $data{max2} mA"
      attr wl_sysmon_power_bat room Technik
      attr wl_sysmon_power_bat group system
=end html =begin html_DE

SYSMON

(en | de)
    Dieses Modul liefert diverse Informationen und Statistiken zu dem System, auf dem FHEM-Server ausgeführt wird. Weiterhin können auch Remote-Systeme abgefragt werden (Telnet). Es werden nur Linux-basierte Systeme unterstützt. Manche Informationen sind hardwarespezifisch und sind daher nicht auf jeder Plattform verfügbar. Bis jetzt wurde dieses Modul auf folgenden Systemen getestet: Raspberry Pi (Debian Wheezy), BeagleBone Black, FritzBox 7390, WR703N unter OpenWrt, CubieTruck und einige andere.

    Für Informationen zu einer FritzBox beachten Sie bitte auch Module: FRITZBOX und FB_CALLMONITOR. Das Modul nutzt das Perlmodule 'Net::Telnet' für den Fernzugriff. Dieses muss ggf. nachinstalliert werden.

    Define

    define <name> SYSMON [MODE[:[USER@]HOST][:PORT]] [<M1>[ <M2>[ <M3>[ <M4>]]]]

    Diese Anweisung erstellt eine neue SYSMON-Instanz. Die Parameter M1 bis M4 legen die Aktualisierungsintervalle für verschiedenen Readings (Statistiken) fest. Die Parameter sind als Multiplikatoren für die Zeit, die durch INTERVAL_BASE definiert ist, zu verstehen. Da diese Zeit fest auf 60 Sekunden gesetzt ist, können die Mx-Parameters als Zeitintervalle in Minuten angesehen werden.
    Wird einer (oder mehrere) Multiplikatoren auf Null gesetzt werden, wird das entsprechende Readings deaktiviert.

    Die Parameter sind für die Aktualisierung der Readings nach folgender Schema zuständig:
    • M1: (Default-Wert: 1)
      cpu_freq, cpu_temp, cpu_temp_avg, loadavg, stat_cpu, stat_cpu_diff, stat_cpu_percent, stat_cpu_text, power readings

    • M2: (Default-Wert: M1)
      ram, swap
    • M3: (Default-Wert: M1)
      eth0, eth0_diff, wlan0, wlan0_diff

    • M4: (Default-Wert: 10*M1)
      Filesystem-Informationen

    • folgende Parameter werden immer anhand des Basisintervalls (unabhängig von den Mx-Parameters) aktualisiert:
      fhemuptime, fhemuptime_text, idletime, idletime_text, uptime, uptime_text, starttime, starttime_text

    Für Abfrage eines entfernten Systems muss mindestens deren Adresse (HOST) angegeben werden, bei Bedarf ergänzt durch den Port und/oder den Benutzernamen. Das eventuell benötigte Passwort muss einmalig mit dem Befehl 'set password <pass>' definiert werden. Als MODE sind derzeit 'telnet', 'ssh' und 'local' erlaubt. 'local' erfordert keine weiteren Angaben und kann auch ganz weggelassen werden.
    Bei SSH-Anmeldung mit Passwort muss 'sshpass' installiert sein (Achtung! Sicherheitstechnisch nicht empfehlenswert! Besser Public-Key-Verfahren benutzen). Damit SSH-Anmeldung funktioniert, muss ggf. einmalig eine manuelle SSH-Verbindung an die Remote-Machine von dem FHEM-Acount (unter dessen Rechten FHEM läuft) durchgeführt und fingerprint bestätigt werden.

    Readings:

    • cpu_core_count
      Anzahl der CPU Kerne
    • cpu_model_name
      CPU Modellname
    • cpu_bogomips
      CPU Speed: BogoMIPS
    • cpu_freq (auf den DualCore-Systemen wie Cubietruck auch cpu1_freq)
      CPU-Frequenz

    • cpu_temp
      CPU-Temperatur

    • cpu_temp_avg
      Durchschnitt der CPU-Temperatur, gebildet über die letzten 4 Werte.

    • fhemuptime
      Zeit (in Sekunden) seit dem Start des FHEM-Servers.

    • fhemuptime_text
      Zeit seit dem Start des FHEM-Servers: Menschenlesbare Ausgabe (texttuelle Darstellung).

    • fhemstarttime
      Startzeit (in Sekunden seit 1.1.1970 1:00:00) des FHEM-Servers.

    • fhemstarttime_text
      Startzeit des FHEM-Servers: Menschenlesbare Ausgabe (texttuelle Darstellung).

    • idletime
      Zeit (in Sekunden und in Prozent), die das System (nicht der FHEM-Server!) seit dem Start in dem Idle-Modus verbracht hat. Also die Zeit der Inaktivität.

    • idletime_text
      Zeit der Inaktivität des Systems seit dem Systemstart in menschenlesbarer Form.

    • loadavg
      Ausgabe der Werte für die Systemauslastung (load average): 1 Minute-, 5 Minuten- und 15 Minuten-Werte.

    • ram
      Ausgabe der Speicherauslastung.

    • swap
      Benutzung und Auslastung der SWAP-Datei (bzw. Partition).

    • uptime
      Zeit (in Sekenden) seit dem Systemstart.

    • uptime_text
      Zeit seit dem Systemstart in menschenlesbarer Form.

    • starttime
      Systemstart (Sekunden seit Thu Jan 1 01:00:00 1970).

    • starttime_text
      Systemstart in menschenlesbarer Form.

    • Netzwerkinformationen
      Informationen zu den über die angegebene Netzwerkschnittstellen übertragene Datenmengen und der Differenz zu der vorherigen Messung.
      Beispiele:
      Menge der übertragenen Daten über die Schnittstelle eth0.
      eth0: RX: 940.58 MB, TX: 736.19 MB, Total: 1676.77 MB
      Änderung der übertragenen Datenmenge in Bezug auf den vorherigen Aufruf (für eth0).
      eth0_diff: RX: 0.66 MB, TX: 0.06 MB, Total: 0.72 MB
      IP and IP v6 Adressen eth0_ip 192.168.0.15
      eth0_ip6 fe85::49:4ff:fe85:f885/64

    • Network Speed (wenn verfügbar)
      Geschwindigkeit der aktuellen Netzwerkverbindung.
      Beispiel:
      eth0_speed 100

    • Dateisysteminformationen
      Informationen zu der Größe und der Belegung der gewünschten Dateisystemen.
      Seit Version 1.1.0 können Dateisysteme auch benannt werden (s.u.).
      In diesem Fall werden für die diese Readings die angegebenen Namen verwendet.
      Dies soll die Übersicht verbessern und die Erstellung von Plots erleichten.
      Beispiel:
      fs_root: Total: 7340 MB, Used: 3573 MB, 52 %, Available: 3425 MB at /

    • CPU Auslastung
      Informationen zu der Auslastung der CPU(s).
      Beispiel:
      stat_cpu: 10145283 0 2187286 90586051 542691 69393 400342
      stat_cpu_diff: 2151 0 1239 2522 10 3 761
      stat_cpu_percent: 4.82 0.00 1.81 93.11 0.05 0.00 0.20
      stat_cpu_text: user: 32.17 %, nice: 0.00 %, sys: 18.53 %, idle: 37.72 %, io: 0.15 %, irq: 0.04 %, sirq: 11.38 %

    • Benutzerdefinierte Einträge
      Diese Readings sind Ausgaben der Kommanden, die an das Betriebssystem übergeben werden. Die entsprechende Angaben werden durch Attributen user-defined und user-fn definiert.

    • FritzBox-spezifische Readings
    • wlan_state
      WLAN-Status: on/off

    • wlan_guest_state
      Gast-WLAN-Status: on/off

    • internet_ip
      aktuelle IP-Adresse

    • internet_state
      Status der Internetverbindung: connected/disconnected

    • night_time_ctrl
      Status der Klingelsperre on/off

    • num_new_messages
      Anzahl der neuen Anrufbeantworter-Meldungen

    • fw_version_info
      Angaben zu der installierten Firmware-Version: <VersionNr> <Erstelldatum> <Zeit>

    • DSL Informationen (FritzBox)
    • dsl_rate
      Down/Up Verbindungsgeschwindigkeit

    • dsl_synctime
      Sync-Zeit mit Vermittlungsstelle

    • dsl_crc_15
      Nicht behebbare Übertragungsfehler in den letzten 15 Minuten

    • dsl_fec_15
      Behebbare Übertragungsfehler in den letzten 15 Minuten

    • Readings zur Stromversorgung
    • power_ac_stat
      Statusinformation für die AC-Buchse: online (0|1), present (0|1), voltage, current
      Beispiel:
      power_ac_stat: 1 1 4.807 264

    • power_ac_text
      Statusinformation für die AC-Buchse in menschenlesbarer Form
      Beispiel:
      power_ac_text ac: present / online, Voltage: 4.807 V, Current: 264 mA

    • power_usb_stat
      Statusinformation für die USB-Buchse

    • power_usb_text
      Statusinformation für die USB-Buchse in menschenlesbarer Form

    • power_battery_stat
      Statusinformation für die Batterie (wenn vorhanden): online (0|1), present (0|1), voltage, current, actual capacity
      Beispiel:
      power_battery_stat: 1 1 4.807 264 100

    • power_battery_text
      Statusinformation für die Batterie (wenn vorhanden) in menschenlesbarer Form

    • power_battery_info
      Menschenlesbare Zusatzinformationen für die Batterie (wenn vorhanden): Technologie, Kapazität, Status, Zustand, Gesamtkapazität
      Beispiel:
      power_battery_info: battery info: Li-Ion , capacity: 100 %, status: Full , health: Good , total capacity: 2100 mAh
      Die Kapazität soll in script.bin (z.B. ct-hdmi.bin) eingestellt werden (Parameter pmu_battery_cap). Mit bin2fex konvertieren (bin2fex -> script.fex -> edit -> fex2bin -> script.bin)

    • cpuX_freq_stat
      Frequenz-Statistik für die CPU X: Minimum, Maximum und Durchschnittswert
      Beispiel:
      cpu0_freq_stat: 100 1000 900

    • cpuX_idle_stat
      Leerlaufzeit-Statistik für die CPU X: Minimum, Maximum und Durchschnittswert
      Beispiel:
      cpu0_freq_stat: 23.76 94.74 90.75

    • cpu[X]_temp_stat
      Temperatur-Statistik für CPU: Minimum, Maximum und Durchschnittswert
      Beispiel:
      cpu_temp_stat: 41.00 42.50 42.00

    • ram_used_stat
      Statistik der RAM-Nutzung: Minimum, Maximum und Durchschnittswert
      Example:
      ram_used_stat: 267.55 1267.75 855.00

    • swap_used_stat
      Statistik der SWAP-Nutzung: Minimum, Maximum und Durchschnittswert
      Example:
      swap_used_stat: 0 1024.00 250.00



    Get:

    • interval
      Listet die bei der Definition angegebene Polling-Intervalle auf.

    • interval_multipliers
      Listet die definierten Multipliers.

    • list
      Gibt alle Readings aus.

    • update
      Aktualisiert alle Readings. Alle Werte werden neu abgefragt.

    • version
      Zeigt die Version des SYSMON-Moduls.


    • list_lan_devices
      Listet bekannte Geräte im LAN (nur FritzBox).

    Set:

    • interval_multipliers
      Definiert Multipliers (wie bei der Definition des Gerätes).

    • clean
      Löscht benutzerdefinierbare Readings. Nach einem Update (oder nach der automatischen Aktualisierung) werden neue Readings generiert.

    • clear <reading name>
      Löscht den Reading-Eintrag mit dem gegebenen Namen. Nach einem Update (oder nach der automatischen Aktualisierung) wird dieser Eintrag ggf. neu erstellt (falls noch definiert). Dieses Mechanismus erlaubt das gezielte Löschen nicht mehr benötigter benutzerdefinierten Einträge.

    • password <Passwort>
      Definiert das Passwort für den Remote-Zugriff (i.d.R. nur einmalig notwendig).


    Attributes:

    • filesystems <reading name>[:<mountpoint>[:<comment>]],...
      Gibt die zu überwachende Dateisysteme an. Es wird eine kommaseparierte Liste erwartet.
      Reading-Name wird bei der Anzeige und Logging verwendet, Mount-Point ist die Grundlage der Auswertung, Kommentar ist relevant für die HTML-Anzeige (s. SYSMON_ShowValuesHTML)
      Beispiel: /boot,/,/media/usb1
      oder: fs_boot:/boot,fs_root:/:Root,fs_usb1:/media/usb1:USB-Stick
      Im Sinne der besseren Übersicht sollten zumindest Name und MountPoint angegeben werden.

    • network-interfaces <name>[:<interface>[:<comment>]],...
      Kommaseparierte Liste der Netzwerk-Interfaces, die überwacht werden sollen. Jeder Eintrag besteht aus dem Reading-Namen, dem Namen des Netwerk-Adapters und einem Kommentar für die HTML-Anzeige (s. SYSMON_ShowValuesHTML). Wird kein Doppelpunkt verwendet, wird der Wert gleichzeitig als Reading-Name und Interface-Name verwendet.
      Beispiel ethernet:eth0:Ethernet,wlan:wlan0:WiFi

    • user-defined <readingsName>:<Interval_Minutes>:<Comment>:<Cmd>,...
      Diese kommaseparierte Liste definiert Einträge mit jeweils folgenden Daten: Reading-Name, Aktualisierungsintervall in Minuten, Kommentar und Betriebssystem-Commando.
      Die BS-Befehle werden entsprechend des angegebenen Intervalls ausgeführt und als Readings mit den angegebenen Namen vermerkt. Kommentare werden für die HTML-Ausgaben (s. SYSMON_ShowValuesHTML) benötigt.
      Alle Parameter sind nicht optional!
      Es ist wichtig, dass die angegebenen Befehle schnell ausgeführt werden, denn in dieser Zeit wird der gesamte FHEM-Server blockiert!
      Werden Ergebnisse der lang laufenden Operationen benötigt, sollten diese z.B als CRON-Job eingerichtet werden und in FHEM nur die davor gespeicherten Ausgaben visualisiert.

      Beispiel: Anzeige der vorliegenden Paket-Aktualisierungen für das Betriebssystem:
      In einem cron-Job wird folgendes täglich ausgeführt:
      sudo apt-get update 2>/dev/null >/dev/null apt-get upgrade --dry-run| perl -ne '/(\d*)\s[upgraded|aktualisiert]\D*(\d*)\D*install|^ \S+.*/ and print "$1 aktualisierte, $2 neue Pakete"' 2>/dev/null > /opt/fhem/data/updatestatus.txt
      Das Attribute uder-defined wird auf
      sys_updates:1440:System Aktualisierungen:cat /opt/fhem/data/updatestatus.txt
      gesetzt. Danach wird die Anzahl der verfügbaren Aktualisierungen täglich als Reading 'sys_updates' protokolliert.

    • user-fn <fn_name>:<Interval_Minutes>:<reading_name1>:<reading_name2>...[:<reading_nameX>],...
      Liste der benutzerdefinierten Perlfunktionen.
      Als <fn_name> können entweder Name einer Perlfunktion oder ein Perlausdruck verwendet werden. Die Perlfunktion bekommt den Device-Hash als Übergabeparameter und muss ein Array mit Werte liefern. Diese Werte werden entsprechend den Parameter <reading_nameX> in Readings übernommen.
      Ein Perlausdruck muss in geschweifte Klammer eingeschlossen werden und kann folgende Paramter verwenden: $HASH (Device-Hash) und $NAME (Device-Name). Rückgabe wird analog einer Perlfunktion erwartet.
      Wichtig! Die Trennung zwischen mehreren Benutzerfunktionen muss mit einem Komma UND einem Leerzeichen erfolgen! Innerhalb der Funktiondefinition dürfen Kommas nicht durch Leerzeichen gefolgt werden.

    • disable
      Mögliche Werte: 0,1. Bei 1 wird die Aktualisierung gestoppt.

    • telnet-prompt-regx, telnet-login-prompt-regx
      RegExp zur Erkennung von Login- und Kommandozeile-Prompt. (Nur für Zugriffe über Telnet relevant.)

    • exclude
      Erlaubt das Abfragen bestimmten Informationen zu unterbinden.
      Mögliche Werte: user-defined (s. user-defined und user-fn), cpucount, uptime, fhemuptime, loadavg, cputemp, cpufreq, cpuinfo, diskstat, cpustat, ramswap, filesystem, network, fbwlan, fbnightctrl, fbnewmessages, fbdecttemp, fbversion, fbdsl, powerinfo

    • ssh-param
      Fügt dem SSH-Aufruf zusätzliche Prameter, wie angegeben hinzu.


    Plots:

      Für dieses Modul sind bereits einige gplot-Dateien vordefiniert:
        FileLog-Versionen:
        SM_RAM.gplot
        SM_CPUTemp.gplot
        SM_FS_root.gplot
        SM_FS_usb1.gplot
        SM_Load.gplot
        SM_Network_eth0.gplot
        SM_Network_eth0t.gplot
        SM_Network_wlan0.gplot
        SM_CPUStat.gplot
        SM_CPUStatSum.gplot
        SM_CPUStatTotal.gplot
        SM_power_ac.gplot
        SM_power_usb.gplot
        SM_power_battery.gplot
        DbLog-Versionen:
        SM_DB_all.gplot
        SM_DB_CPUFreq.gplot
        SM_DB_CPUTemp.gplot
        SM_DB_Load.gplot
        SM_DB_Network_eth0.gplot
        SM_DB_RAM.gplot

    HTML-Ausgabe-Methode (für ein Weblink): SYSMON_ShowValuesHTML(<SYSMON-Instanz>[,<Liste>])

      Das Modul definiert eine Funktion, die ausgewählte Readings in HTML-Format ausgibt.
      Als Parameter wird der Name des definierten SYSMON-Geräts erwartet.
      Es kann auch ReadingsGroup, CloneDummy oder andere Module genutzt werden, dann werden einfach deren Readings verwendet.
      Der zweite Parameter ist optional und gibt eine Liste der anzuzeigende Readings im Format <ReadingName>[:<Comment>[:<Postfix>[:<FormatString>]]] an.
      Dabei gibt ReadingName den anzuzeigenden Reading an, der Wert aus Comment wird als der Anzeigename verwendet und Postfix wird nach dem eihentlichen Wert angezeigt (so können z.B. Einheiten wie MHz angezeigt werden). Mit Hilfe von FormatString kann die Ausgabe beeinflusst werden (s. sprintf in PerlDoku).
      Falls kein Comment angegeben ist, wird eine intern vordefinierte Beschreibung angegeben. Bei benutzerdefinierbaren Readings wird ggf. Comment aus der Definition verwendet.
      Wird keine Liste angegeben, wird eine vordefinierte Auswahl verwendet (alle Werte).

      define sysv1 weblink htmlCode {SYSMON_ShowValuesHTML('sysmon')}
      define sysv2 weblink htmlCode {SYSMON_ShowValuesHTML('sysmon', ('date:Datum', 'cpu_temp:CPU Temperatur: °C', 'cpu_freq:CPU Frequenz: MHz'))}

    HTML-Ausgabe-Methode (für ein Weblink): SYSMON_ShowValuesHTMLTitled(<SYSMON-Instance>[,<Title>,<Liste>])

      Wie SYSMON_ShowValuesHTML, aber mit einer Überschrift darüber. Wird keine Überschrift angegeben, wird alias des Moduls genutzt (falls definiert).

    Text-Ausgabe-Methode (see Weblink): SYSMON_ShowValuesText(<SYSMON-Instance>[,<Liste>])

      Analog SYSMON_ShowValuesHTML, jedoch formatiert als reines Text.

    HTML-Ausgabe-Methode (für ein Weblink): SYSMON_ShowValuesTextTitled(<SYSMON-Instance>[,<Title>,<Liste>])

      Wie SYSMON_ShowValuesText, aber mit einer Überschrift darüber.

    Readings-Werte mit Perl lesen: SYSMON_getValues(<name>[, <Liste der gewünschten Schlüssel>])

      Liefert ein Hash-Ref mit den gewünschten Werten. Wenn keine Liste (array) übergeben wird, werden alle Werte geliefert.
      {(SYSMON_getValues("sysmon"))->{'cpu_temp'}}
      {(SYSMON_getValues("sysmon",("cpu_freq","cpu_temp")))->{"cpu_temp"}}
      {join(" ", values (SYSMON_getValues("sysmon")))}
      {join(" ", values (SYSMON_getValues("sysmon",("cpu_freq","cpu_temp"))))}

    Beispiele:

      # Modul-Definition
      define sysmon SYSMON 1 1 1 10
      #attr sysmon event-on-update-reading cpu_temp,cpu_temp_avg,cpu_freq,eth0_diff,loadavg,ram,^~ /.*usb.*,~ /$
      attr sysmon event-on-update-reading cpu_temp,cpu_temp_avg,cpu_freq,eth0_diff,loadavg,ram,fs_.*,stat_cpu_percent
      attr sysmon filesystems fs_boot:/boot,fs_root:/:Root,fs_usb1:/media/usb1:USB-Stick
      attr sysmon network-interfaces eth0:eth0:Ethernet,wlan0:wlan0:WiFi
      attr sysmon group RPi
      attr sysmon room 9.03_Tech

      # Log
      define FileLog_sysmon FileLog ./log/sysmon-%Y-%m.log sysmon
      attr FileLog_sysmon group RPi
      attr FileLog_sysmon logtype SM_CPUTemp:Plot,text
      attr FileLog_sysmon room 9.03_Tech

      # Visualisierung: CPU-Temperatur
      define wl_sysmon_temp SVG FileLog_sysmon:SM_CPUTemp:CURRENT
      attr wl_sysmon_temp group RPi
      attr wl_sysmon_temp label "CPU Temperatur: Min $data{min2}, Max $data{max2}, Last $data{currval2}"
      attr wl_sysmon_temp room 9.03_Tech

      # Visualisierung: Netzwerk-Datenübertragung für eth0
      define wl_sysmon_eth0 SVG FileLog_sysmon:SM_Network_eth0:CURRENT
      attr wl_sysmon_eth0 group RPi
      attr wl_sysmon_eth0 label "Netzwerk-Traffic eth0: $data{min1}, Max: $data{max1}, Aktuell: $data{currval1}"
      attr wl_sysmon_eth0 room 9.03_Tech

      # Visualisierung: Netzwerk-Datenübertragung für wlan0
      define wl_sysmon_wlan0 SVG FileLog_sysmon:SM_Network_wlan0:CURRENT
      attr wl_sysmon_wlan0 group RPi
      attr wl_sysmon_wlan0 label "Netzwerk-Traffic wlan0: $data{min1}, Max: $data{max1}, Aktuell: $data{currval1}"
      attr wl_sysmon_wlan0 room 9.03_Tech

      # Visualisierung: CPU-Auslastung (load average)
      define wl_sysmon_load SVG FileLog_sysmon:SM_Load:CURRENT
      attr wl_sysmon_load group RPi
      attr wl_sysmon_load label "Load Min: $data{min1}, Max: $data{max1}, Aktuell: $data{currval1}"
      attr wl_sysmon_load room 9.03_Tech

      # Visualisierung: RAM-Nutzung
      define wl_sysmon_ram SVG FileLog_sysmon:SM_RAM:CURRENT
      attr wl_sysmon_ram group RPi
      attr wl_sysmon_ram label "RAM-Nutzung Total: $data{max1}, Min: $data{min2}, Max: $data{max2}, Aktuell: $data{currval2}"
      attr wl_sysmon_ram room 9.03_Tech

      # Visualisierung: Dateisystem: Root-Partition
      define wl_sysmon_fs_root SVG FileLog_sysmon:SM_FS_root:CURRENT
      attr wl_sysmon_fs_root group RPi
      attr wl_sysmon_fs_root label "Root Partition Total: $data{max1}, Min: $data{min2}, Max: $data{max2}, Aktuell: $data{currval2}"
      attr wl_sysmon_fs_root room 9.03_Tech

      # Visualisierung: Dateisystem: USB-Stick
      define wl_sysmon_fs_usb1 SVG FileLog_sysmon:SM_FS_usb1:CURRENT
      attr wl_sysmon_fs_usb1 group RPi
      attr wl_sysmon_fs_usb1 label "USB1 Total: $data{max1}, Min: $data{min2}, Max: $data{max2}, Aktuell: $data{currval2}"
      attr wl_sysmon_fs_usb1 room 9.03_Tech

      # Anzeige der Readings zum Einbinden in ein 'Raum'.
      define SysValues weblink htmlCode {SYSMON_ShowValuesHTML('sysmon')}
      attr SysValues group RPi
      attr SysValues room 9.03_Tech

      # Anzeige CPU Auslasung
      define wl_sysmon_cpustat SVG FileLog_sysmon:SM_CPUStat:CURRENT
      attr wl_sysmon_cpustat label "CPU(min/max): user:$data{min1}/$data{max1} nice:$data{min2}/$data{max2} sys:$data{min3}/$data{max3} idle:$data{min4}/$data{max4} io:$data{min5}/$data{max5} irq:$data{min6}/$data{max6} sirq:$data{min7}/$data{max7}"
      attr wl_sysmon_cpustat group RPi
      attr wl_sysmon_cpustat room 9.99_Test
      attr wl_sysmon_cpustat plotsize 840,420
      define wl_sysmon_cpustat_s SVG FileLog_sysmon:SM_CPUStatSum:CURRENT
      attr wl_sysmon_cpustat_s label "CPU(min/max): user:$data{min1}/$data{max1} nice:$data{min2}/$data{max2} sys:$data{min3}/$data{max3} idle:$data{min4}/$data{max4} io:$data{min5}/$data{max5} irq:$data{min6}/$data{max6} sirq:$data{min7}/$data{max7}"
      attr wl_sysmon_cpustat_s group RPi
      attr wl_sysmon_cpustat_s room 9.99_Test
      attr wl_sysmon_cpustat_s plotsize 840,420
      define wl_sysmon_cpustatT SVG FileLog_sysmon:SM_CPUStatTotal:CURRENT
      attr wl_sysmon_cpustatT label "CPU-Auslastung"
      attr wl_sysmon_cpustatT group RPi
      attr wl_sysmon_cpustatT plotsize 840,420
      attr wl_sysmon_cpustatT room 9.99_Test

      # Anzeige Stromversorgung AC
      define wl_sysmon_power_ac SVG FileLog_sysmon:SM_power_ac:CURRENT
      attr wl_sysmon_power_ac label "Stromversorgung (ac) Spannung: $data{min1} - $data{max1} V, Strom: $data{min2} - $data{max2} mA"
      attr wl_sysmon_power_ac room Technik
      attr wl_sysmon_power_ac group system
      # Anzeige Stromversorgung Battery
      define wl_sysmon_power_bat SVG FileLog_sysmon:SM_power_battery:CURRENT
      attr wl_sysmon_power_bat label "Stromversorgung (bat) Spannung: $data{min1} - $data{max1} V, Strom: $data{min2} - $data{max2} mA"
      attr wl_sysmon_power_bat room Technik
      attr wl_sysmon_power_bat group system
=end html_DE =cut