2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00
fhem-mirror/fhem/FHEM/72_FB_CALLLIST.pm

1435 lines
59 KiB
Perl
Executable File

# $Id$
##############################################################################
#
# 72_FB_CALLLIST.pm
# Creates a call list based on the events generated by a FB_CALLMONITOR instance
#
# Copyright by Markus Bloch
# e-mail: Notausstieg0309@googlemail.com
#
# This file is part of fhem.
#
# Fhem 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.
#
# Fhem 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.
#
# You should have received a copy of the GNU General Public License
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
package main;
use strict;
use warnings;
use MIME::Base64;
use Data::Dumper;
sub
FB_CALLLIST_Initialize($)
{
my ($hash) = @_;
$hash->{SetFn} = "FB_CALLLIST_Set";
$hash->{DefFn} = "FB_CALLLIST_Define";
$hash->{NotifyFn} = "FB_CALLLIST_Notify";
$hash->{AttrFn} = "FB_CALLLIST_Attr";
$hash->{AttrList} = "number-of-calls:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 ".
"internal-number-filter ".
"icon-mapping ".
"connection-mapping ".
"external-mapping ".
"create-readings:0,1 ".
"visible-columns:sortable-strict,row,state,timestamp,name,number,internal,external,connection,duration ".
"show-icons:1,0 ".
"list-type:all,incoming,outgoing,missed-calls,completed,active " .
"time-format-string ".
"list-order:ascending,descending ".
"language:de,en ".
"disable:0,1 ".
"disabledForIntervals ".
"do_not_notify:0,1 ".
"no-heading:0,1 ".
$readingFnAttributes;
$hash->{FW_detailFn} = "FB_CALLLIST_makeTable";
$hash->{FW_summaryFn} = "FB_CALLLIST_makeTable";
$hash->{FW_atPageEnd} = 1;
}
#####################################
# Define function
sub FB_CALLLIST_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
my $retval = undef;
my $name = $a[0];
if(!defined($a[2]))
{
return "FB_CALLLIST_define: you must specify a device name for using FB_CALLLIST";
}
if(@a != 3)
{
return "wrong define syntax: define <name> FB_CALLLIST <name>";
}
unless(defined($defs{$a[2]}))
{
return "FB_CALLLIST_define: the selected device ".$a[2]." does not exist.";
}
unless($defs{$a[2]}->{TYPE} eq "FB_CALLMONITOR")
{
Log3 $name, 3, "FB_CALLLIST ($name) - WARNING - selected device ".$a[2]." ist not of type FB_CALLMONITOR";
}
$hash->{FB} = $a[2];
$hash->{NOTIFYDEV} = $a[2];
$hash->{STATE} = 'Initialized';
$hash->{helper}{DEFAULT_COLUMN_ORDER} = "row,state,timestamp,name,number,internal,external,connection,duration";
FB_CALLLIST_loadList($hash);
return undef;
}
#####################################
# AttrFn for importing filter expressions and cleanup list when user set an attribute
sub FB_CALLLIST_Attr($@)
{
my ($cmd, $name, $attrib, $value) = @_;
my $hash = $defs{$name};
if($cmd eq "set")
{
if($attrib eq "internal-number-filter")
{
if( $value =~ m/^{.*}$/ )
{
my $table = eval $value;
if($table and (ref($table) eq 'HASH'))
{
$hash->{helper}{INTERNAL_FILTER} = $table;
Log3 $name, 4, "FB_CALLLIST ($name) - filter stored as hash: $value";
}
else
{
return "(line-filter) is not a valid hash: $value";
}
}
else
{
my %lines;
foreach my $item (split("[ \t,][ \t,]*",$value))
{
$lines{$item} = $item;
}
$hash->{helper}{INTERNAL_FILTER} = \%lines;
Log3 $name, 4, "FB_CALLLIST ($name) - filter stored as list $value";
}
if($init_done)
{
# delete all outdated calls according to attribute list-type, internal-number-filter and number-of-calls
FB_CALLLIST_cleanupList($hash);
# Inform all FHEMWEB clients
FB_CALLLIST_updateFhemWebClients($hash);
}
}
elsif($attrib eq "connection-mapping")
{
if($value and $value =~ m/^{.*}$/ )
{
my $table = eval $value;
if($table and ref($table) eq 'HASH')
{
$hash->{helper}{CONNECTION_MAP} = $table;
Log3 $name, 4, "FB_CALLLIST ($name) - connection map stored as hash: $value";
# Inform all FHEMWEB clients
FB_CALLLIST_updateFhemWebClients($hash) if($init_done);
}
else
{
return "invalid connection mapping table: $value";
}
}
else
{
return "invalid connection mapping table: $value";
}
}
elsif($attrib eq "icon-mapping")
{
if($value and $value =~ m/^{.*}$/ )
{
$value =~ s/"/'/g; # workaround for array variable interpretation
my $table = eval $value;
if($table and ref($table) eq 'HASH')
{
$hash->{helper}{ICON_MAP} = $table;
Log3 $name, 4, "FB_CALLLIST ($name) - icon map stored as hash: $value";
# Inform all FHEMWEB clients
FB_CALLLIST_updateFhemWebClients($hash) if($init_done);
}
else
{
return "invalid icon mapping table: $value";
}
}
else
{
return "invalid icon mapping table: $value";
}
}
elsif($attrib eq "external-mapping")
{
if($value and $value =~ m/^{.*}$/ )
{
my $table = eval $value;
if($table and ref($table) eq 'HASH')
{
$hash->{helper}{EXTERNAL_MAP} = $table;
Log3 $name, 4, "FB_CALLLIST ($name) - external map stored as hash: $value";
# Inform all FHEMWEB clients
FB_CALLLIST_updateFhemWebClients($hash) if($init_done);
}
else
{
return "invalid external mapping table: $value";
}
}
else
{
return "invalid external mapping table: $value";
}
}
elsif($attrib eq "list-type")
{
if($value and $value =~ /^incoming|outgoing|missed-call|completed|active$/)
{
$attr{$name}{$attrib} = $value;
if($init_done)
{
# delete all outdated calls according to attribute list-type, internal-number-filter and number-of-calls
FB_CALLLIST_cleanupList($hash);
# Inform all FHEMWEB clients
FB_CALLLIST_updateFhemWebClients($hash);
}
}
}
}
elsif($cmd eq "del")
{
if($attrib eq "internal-number-filter")
{
delete($hash->{helper}{INTERNAL_FILTER}) if(exists($hash->{helper}{INTERNAL_FILTER}));
return undef;
}
elsif($attrib eq "connection-mapping")
{
delete($hash->{helper}{CONNECTION_MAP}) if(exists($hash->{helper}{CONNECTION_MAP}));
return undef;
}
elsif($attrib eq "icon-mapping")
{
delete($hash->{helper}{ICON_MAP}) if(exists($hash->{helper}{ICON_MAP}));
return undef;
}
elsif($attrib eq "external-mapping")
{
delete($hash->{helper}{EXTERNAL_MAP}) if(exists($hash->{helper}{EXTERNAL_MAP}));
return undef;
}
}
}
#####################################
# SetFn for clearing the list
sub FB_CALLLIST_Set($@)
{
my ($hash, $name, $cmd, $value) = @_;
my $usage = "Unknown argument $cmd, choose one of clear:noArg";
if($cmd eq "clear")
{
delete($hash->{helper}{DATA}) if(exists($hash->{helper}{DATA}));
# Inform all FHEMWEB clients
FB_CALLLIST_updateFhemWebClients($hash);
# Delete stored list
FB_CALLLIST_saveList($hash);
return undef;
}
else
{
return $usage;
}
}
#####################################
# NotifyFn is trigger upon changes on FB_CALLMONITOR device. Imports the call data into call list
sub FB_CALLLIST_Notify($$)
{
my ($hash,$d) = @_;
return undef if(!defined($hash));
my $name = $hash->{NAME};
return undef if(IsDisabled($name));
my $fb = $d->{NAME};
if ($fb ne $hash->{FB})
{
return undef;
}
my $event = ReadingsVal($fb, "event", undef);
my $call_id = ReadingsVal($fb, "call_id", undef);
Log3 $name, 4, "FB_CALLLIST ($name) - start processing event $event for Call-ID $call_id";
if(exists($hash->{helper}{LAST_EVENT}) and exists($hash->{helper}{LAST_CALL_ID}) and $event eq $hash->{helper}{LAST_EVENT} and $call_id eq $hash->{helper}{LAST_CALL_ID})
{
Log3 $name, 4, "FB_CALLLIST ($name) - already processed event $event for Call-ID $call_id, skipping...";
return undef
}
else
{
$hash->{helper}{LAST_EVENT} = $event;
$hash->{helper}{LAST_CALL_ID} = $call_id;
}
if(exists($hash->{helper}{INTERNAL_FILTER}))
{
Log3 $name, 5, "FB_CALLLIST ($name) - internal-number-filter is defined, checking if internal number is allowed";
my $line_number = ReadingsVal($fb, "internal_number", undef);
if(defined($line_number) and not exists($hash->{helper}{INTERNAL_FILTER}{$line_number}))
{
Log3 $name, 5, "FB_CALLLIST ($name) - internal number $line_number does not match the current internal-number-filter. skipping call.";
return undef;
}
Log3 $name, 5, "FB_CALLLIST ($name) - call passed the internal-number-filter. proceeding...";
}
my $data;
if($event =~ /^call|ring$/)
{
my $timestamp = gettimeofday();
$hash->{helper}{DATA}{$timestamp} = undef;
$data = \%{$hash->{helper}{DATA}{$timestamp}};
$data->{external_number} = ReadingsVal($fb, "external_number", undef);
$data->{external_name} = ReadingsVal($fb, "external_name", undef);
$data->{external_connection} = ReadingsVal($fb, "external_connection", undef);
$data->{internal_number} = ReadingsVal($fb, "internal_number", undef);
$data->{direction} = ReadingsVal($fb, "direction", undef);
$data->{running_call} = 1;
$data->{call_id} = $call_id;
$data->{last_event} = $event;
if($data->{direction} eq "outgoing")
{
$data->{internal_connection} = ReadingsVal($fb, "internal_connection", undef);
}
Log3 $name, 5, "FB_CALLLIST ($name) - created new data hash: $data";
}
else
{
$data = FB_CALLLIST_getDataReference($hash, $call_id);
Log3 $name, 5, "FB_CALLLIST ($name) - found old data hash: $data" if($data);
}
if(!$data)
{
Log3 $name, 4, "FB_CALLLIST ($name) - no data for this call in list. seams to be filtered out. skipping further processing...";
return undef;
}
if($event eq "connect")
{
$data->{internal_connection} = ReadingsVal($fb, "internal_connection", undef) if($data->{direction} eq "incoming");
$data->{last_event} = $event;
Log3 $name, 5, "FB_CALLLIST ($name) - processed connect event for call id $call_id";
}
if($event eq "disconnect" )
{
$data->{call_duration} = ReadingsVal($fb, "call_duration", undef);
if($data->{last_event} =~ /^call|ring$/)
{
$data->{missed_call} = 1;
}
$data->{last_event} = $event;
delete($data->{running_call}) if(defined($data->{running_call}));
Log3 $name, 5, "FB_CALLLIST ($name) - processed disconnect event for call id $call_id";
}
# clean up the list
FB_CALLLIST_cleanupList($hash);
# Inform all FHEMWEB clients
FB_CALLLIST_updateFhemWebClients($hash);
# save current list state to file/configDB
FB_CALLLIST_saveList($hash);
}
#####################################
# returns a hash reference to the data set of a specific running call
sub FB_CALLLIST_getDataReference($$)
{
my ($hash, $call_id) = @_;
my @result = grep {$hash->{helper}{DATA}{$_}{call_id} eq $call_id and defined($hash->{helper}{DATA}{$_}{running_call}) and $hash->{helper}{DATA}{$_}{running_call} == 1} keys %{$hash->{helper}{DATA}};
return \%{$hash->{helper}{DATA}{$result[0]}} if(exists($result[0]));
return undef;
}
#####################################
# cleans up the list from all unwanted entries
sub FB_CALLLIST_cleanupList($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $limit = int(AttrVal($hash->{NAME}, "number-of-calls", 5));
my $listtype = AttrVal($hash->{NAME}, "list-type", "all");
my $count = 0;
my $index;
my @list;
if(exists($hash->{helper}{DATA}) and (scalar keys %{$hash->{helper}{DATA}}) > 0)
{
Log3 $name, 4, "FB_CALLLIST ($name) - cleaning up call list";
# delete calls which not matched the configured list-type and number-of-calls
if($listtype ne "all")
{
@list = grep { ($hash->{helper}{DATA}{$_}{direction} ne "incoming") or ($hash->{helper}{DATA}{$_}{direction} eq "incoming" and ++$count > $limit) } sort {$b <=> $a} keys %{$hash->{helper}{DATA}} if($listtype eq "incoming");
@list = grep { ($hash->{helper}{DATA}{$_}{direction} ne "outgoing") or ($hash->{helper}{DATA}{$_}{direction} eq "outgoing" and ++$count > $limit) } sort {$b <=> $a} keys %{$hash->{helper}{DATA}} if($listtype eq "outgoing");
@list = grep { ((not $hash->{helper}{DATA}{$_}{running_call}) and (not $hash->{helper}{DATA}{$_}{missed_call}) or $hash->{helper}{DATA}{$_}{direction} eq "outgoing") or ($hash->{helper}{DATA}{$_}{direction} eq "incoming" and $hash->{helper}{DATA}{$_}{missed_call} and ++$count > $limit) } sort {$b <=> $a} keys %{$hash->{helper}{DATA}} if($listtype eq "missed-calls");
@list = grep { (not $hash->{helper}{DATA}{$_}{running_call}) and ++$count > $limit } sort {$b <=> $a} keys %{$hash->{helper}{DATA}} if($listtype eq "completed");
@list = grep { (not $hash->{helper}{DATA}{$_}{running_call}) or ($hash->{helper}{DATA}{$_}{running_call} and ++$count > $limit)} sort {$b <=> $a} keys %{$hash->{helper}{DATA}} if($listtype eq "active");
}
else
{
@list = grep { ++$count > $limit } sort {$b <=> $a} keys %{$hash->{helper}{DATA}};
}
# delete calls which do not match the configured internal-number-filter
if(exists($hash->{helper}{INTERNAL_FILTER}))
{
push @list, grep { not FB_CALLLIST_checkForInternalNumberFilter($hash, $hash->{helper}{DATA}{$_}{internal_number}) } keys %{$hash->{helper}{DATA}};
}
# delete the collected list of unwanted calls
foreach $index (@list)
{
Log3 $name, 5, "FB_CALLLIST ($name) - deleting old call $index";
delete($hash->{helper}{DATA}{$index}) if(exists($hash->{helper}{DATA}{$index}));
}
}
else
{
Log3 $name, 4, "FB_CALLLIST ($name) - list is empty. no cleanup needed";
}
}
#####################################
# returns the icon depending on icon mapping
sub FB_CALLLIST_returnIcon($$$)
{
my ($hash, $icon, $text) = @_;
my $icon_name;
my $standard = {
"incoming.connected" => "phone_ring_in\@blue",
"outgoing.connected" => "phone_ring_out\@green",
"incoming.ring" => "phone_ring\@blue",
"outgoing.ring" => "phone_ring\@green",
"incoming.missed" => "phone_missed_in\@red",
"outgoing.missed" => "phone_missed_out\@green",
"incoming.done" => "phone_call_end_in\@blue",
"outgoing.done" => "phone_call_end_out\@green",
"incoming.tam" => "phone_answering\@blue"
};
$icon_name = $standard->{$icon} if(exists($standard->{$icon}));
$icon_name = $hash->{helper}{ICON_MAP}{$icon} if(exists($hash->{helper}{ICON_MAP}{$icon}));
my $result = FW_makeImage($icon_name);
return $result if($result ne $icon_name);
return $text;
}
#####################################
# returns the call state of a specific call as icon or text
sub FB_CALLLIST_returnCallState($$;$)
{
my ($hash, $index, $icons) = @_;
return undef unless(exists($hash->{helper}{DATA}{$index}));
my $data = \%{$hash->{helper}{DATA}{$index}};
my $state;
$icons = AttrVal($hash->{NAME}, "show-icons", 1) unless(defined($icons));
if($data->{running_call})
{
if($data->{direction} eq "incoming" and $data->{last_event} eq "connect" )
{
$state = "=> [=]";
$state = FB_CALLLIST_returnIcon($hash,"incoming.connected", $state) if($icons);
}
elsif($data->{direction} eq "incoming" and $data->{last_event} eq "ring")
{
$state = "=> ((o))";
$state = FB_CALLLIST_returnIcon($hash,"incoming.ring", $state) if($icons);
}
elsif($data->{direction} eq "outgoing" and $data->{last_event} eq "connect" )
{
$state = "<= [=]";
$state = FB_CALLLIST_returnIcon($hash,"outgoing.connected", $state) if($icons);
}
elsif($data->{direction} eq "outgoing" and $data->{last_event} eq "call")
{
$state = "<= ((o))";
$state = FB_CALLLIST_returnIcon($hash,"outgoing.ring", $state) if($icons);
}
}
else
{
if($data->{direction} eq "incoming" and ((not exists($data->{internal_connection}) ) or (exists($data->{internal_connection}) and not $data->{internal_connection} =~ /Answering_Machine/)))
{
$state = "=>".($data->{missed_call} ? " X" : "");
$state = FB_CALLLIST_returnIcon($hash, "incoming.done", $state) if($icons and not $data->{missed_call});
$state = FB_CALLLIST_returnIcon($hash, "incoming.missed", $state) if($icons and $data->{missed_call});
}
elsif($data->{direction} eq "incoming" and exists($data->{internal_connection}) and $data->{internal_connection} =~ /Answering_Machine/)
{
$state = "=> O_O";
$state = FB_CALLLIST_returnIcon($hash,"incoming.tam", $state) if($icons);
}
elsif($data->{direction} eq "outgoing")
{
$state = "<=".($data->{missed_call} ? " X" : "");
$state = FB_CALLLIST_returnIcon($hash, "outgoing.done", $state) if($icons and not $data->{missed_call});
$state = FB_CALLLIST_returnIcon($hash, "outgoing.missed", $state) if($icons and $data->{missed_call});
}
}
return $state;
}
#####################################
# FW_detailFn & FW_summaryFn handler for creating the html output in FHEMWEB
sub FB_CALLLIST_makeTable($$$$)
{
my ($FW_wname, $devname, $room, $extPage) = @_;
my $hash = $defs{$devname};
return FB_CALLLIST_list2html($hash)
}
#####################################
# creating the call list as html string or json array
sub FB_CALLLIST_list2html($;$)
{
my ($hash, $to_json) = @_;
return undef if( !$hash );
my $name = $hash->{NAME};
my $alias = AttrVal($hash->{NAME}, "alias", $hash->{NAME});
my $create_readings = AttrVal($hash->{NAME}, "create-readings",0);
my $td_style = "style=\"padding-left:6px;padding-right:6px;\"";
my @json_output = ();
my $line;
my $old_locale = setlocale(LC_ALL);
my $ret .= "<table>";
if(AttrVal($name, "no-heading", "0") eq "0")
{
$ret .=" <tr><td>";
$ret .= "<div class=\"devType\"><a href=\"$FW_ME$FW_subdir?detail=$name\">$alias</a>".(IsDisabled($name) ? " (disabled)" : "")."</div>" unless($FW_webArgs{"detail"});
$ret .= "</td></tr>";
}
$ret .= "<tr><td>";
$ret .= "<div class=\"fhemWidget\" informId=\"$name\" cmd=\"\" arg=\"fbcalllist\" dev=\"$name\">"; # div tag to support inform updates
$ret .= "<table class=\"block fbcalllist\">";
$ret .= FB_CALLLIST_returnOrderedHTMLOutput($hash, FB_CALLLIST_returnTableHeader($hash), "class=\"fbcalllist header\"","");
if(exists($hash->{helper}{DATA}) and (scalar keys %{$hash->{helper}{DATA}}) > 0)
{
my $count = 1;
my @json_list;
my @list = sort { if(AttrVal($name, "list-order","descending") eq "descending") {return $b <=> $a;} else {return $a <=> $b;} } keys %{$hash->{helper}{DATA}};
if(AttrVal($hash->{NAME}, "list-type", "all") eq "missed-calls")
{
@list = grep { !$hash->{helper}{DATA}{$_}{running_call} } @list;
}
if(AttrVal($hash->{NAME}, "list-type", "all") eq "completed")
{
@list = grep { !$hash->{helper}{DATA}{$_}{running_call} } @list;
}
foreach my $index (@list)
{
my $data = \%{$hash->{helper}{DATA}{$index}};
my $state = FB_CALLLIST_returnCallState($hash, $index);
my $time = strftime(AttrVal($name, "time-format-string", "%a, %d %b %Y %H:%M:%S"), localtime($index));
my $name = ($data->{external_name} eq "unknown" ? "-" : $data->{external_name});
my $number = $data->{external_number};
my $external = ($data->{external_connection} ? ((exists($hash->{helper}{EXTERNAL_MAP}) and exists($hash->{helper}{EXTERNAL_MAP}{$data->{external_connection}})) ? $hash->{helper}{EXTERNAL_MAP}{$data->{external_connection}} : $data->{external_connection} ) : "-");
my $internal = ((exists($hash->{helper}{INTERNAL_FILTER}) and exists($hash->{helper}{INTERNAL_FILTER}{$data->{internal_number}})) ? $hash->{helper}{INTERNAL_FILTER}{$data->{internal_number}} : $data->{internal_number} );
my $connection = ($data->{internal_connection} ? ((exists($hash->{helper}{CONNECTION_MAP}) and exists($hash->{helper}{CONNECTION_MAP}{$data->{internal_connection}})) ? $hash->{helper}{CONNECTION_MAP}{$data->{internal_connection}} : $data->{internal_connection} ) : "-");
my $duration = FB_CALLLIST_formatDuration($hash, $index);
$line = {
index => $index,
line => $count,
row => $count,
state => $state,
timestamp => $time,
name => $name,
number => $number,
external => $external,
internal => $internal,
connection => $connection,
duration => $duration
};
push @json_output, FB_CALLLIST_returnOrderedJSONOutput($hash, $line);
FB_CALLLIST_updateReadings($hash, $line) if($to_json and $create_readings);
$ret .= FB_CALLLIST_returnOrderedHTMLOutput($hash, $line, "number=\"$count\" class=\"fbcalllist ".($count % 2 == 1 ? "odd" : "even")."\"", "class=\"fbcalllist\" $td_style");
$count++;
}
}
else
{
my $string;
if(AttrVal($name, "language", "en") eq "de")
{
$string = "leer";
}
else
{
$string = "empty";
}
my @columns = split(",",AttrVal($name, "visible-columns", $hash->{helper}{DEFAULT_COLUMN_ORDER}));
my $additional_columns = scalar(@columns);
$ret .= "<tr align=\"center\" name=\"empty\"><td style=\"padding:10px;\" colspan=\"$additional_columns\"><i>$string</i></td></tr>";
}
$ret .= "</table></div>";
$ret .= "</td></tr></table>";
setlocale(LC_ALL, $old_locale);
return ($to_json ? @json_output : $ret);
}
#####################################
# format duration in seconds into hh:mm:ss
sub FB_CALLLIST_formatDuration($$)
{
my ($hash, $index) = @_;
my $data = \%{$hash->{helper}{DATA}{$index}};
if($data->{running_call})
{
if(AttrVal($hash->{NAME}, "language", "en") eq "de")
{
return "<i>l&auml;uft</i>";
}
else
{
return "<i>ongoing</i>";
}
}
my $hour = int($data->{call_duration} / (60 * 60));
my $minute = ($data->{call_duration} / 60) % 60;
my $seconds = int($data->{call_duration} % 60);
if($data->{missed_call})
{
return "-";
}
else
{
return sprintf("%02d:%02d:%02d", $hour, $minute, $seconds);
}
}
#####################################
# save the current call list to file or configDB
sub FB_CALLLIST_saveList($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
if(exists($hash->{helper}{DATA}))
{
Log3 $name, 5, "FB_CALLLIST ($name) - start dumping of list to file";
my $dumper = Data::Dumper->new([$hash->{helper}{DATA}], [qw($hash->{helper}{DATA})] );
$dumper->Purity(1);
$dumper->Terse(0);
my $dump = $dumper->Dump;
eval { require Compress::Zlib; };
unless($@)
{
Log3 $name, 5, "FB_CALLLIST ($name) - found Compress::Zlib module, compressing dump";
$dump = Compress::Zlib::compress($dump);
$dump = "compressed:".encode_base64($dump, "");
}
else
{
Log3 $name, 5, "FB_CALLLIST ($name) - unable to load Compress::Zlib module: $@";
Log3 $name, 5, "FB_CALLLIST ($name) - using just plain base64 encoding for dump";
$dump = encode_base64($dump, "");
}
Log3 $name, 5, "FB_CALLLIST ($name) - saving list dump: ".$dump;
my $err = setKeyValue("FB_CALLLIST-$name", $dump);
Log3 $name, 3, "FB_CALLLIST ($name) - error while saving the current call list: $err" if(defined($err));
}
else
{
my $err = setKeyValue("FB_CALLLIST-$name", undef);
Log3 $name, 3, "FB_CALLLIST ($name) - error while saving the current call list: $err" if(defined($err));
}
}
#####################################
# load the call list from file or configDB
sub FB_CALLLIST_loadList($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "FB_CALLLIST ($name) - loading old call list from file";
delete($hash->{helper}{DATA}) if(exists($hash->{helper}{DATA}));
my ($err, $dump) = getKeyValue("FB_CALLLIST-$name");
if(defined($err))
{
Log3 $name, 3, "FB_CALLLIST ($name) - error while loading the old call list state: $err";
return undef;
}
if(defined($dump))
{
if($dump =~ /^compressed:(.+)$/)
{
Log3 $name, 5, "FB_CALLLIST ($name) - found compressed list dump in file";
$dump = $1;
eval { require Compress::Zlib; };
unless($@)
{
$dump = decode_base64($dump);
$dump = Compress::Zlib::uncompress($dump);
}
else
{
Log3 $name, 3, "FB_CALLLIST ($name) - unable to load module Compress::Zlib to unpack compressed old call list: $@";
return undef;
}
}
else
{
$dump = decode_base64($dump);
}
Log3 $name, 5, "FB_CALLLIST ($name) - importing list...\n$dump";
eval($dump);
Log3 $name, 3, "FB_CALLLIST ($name) - error while importing old call list state: $@" if($@);
}
else
{
Log3 $name, 5, "FB_CALLLIST ($name) - no list found for restoring";
}
}
#####################################
# produce a HTML <tr>-Output for a specific data set depending on visible-columns setting
sub FB_CALLLIST_returnOrderedHTMLOutput($$$$)
{
my ($hash,$line, $tr_additions, $td_additions) = @_;
my $name = $hash->{NAME};
my @order = split(",", AttrVal($name, "visible-columns",$hash->{helper}{DEFAULT_COLUMN_ORDER}));
my @ret = ();
push @ret, "<tr align=\"center\" $tr_additions>";
foreach my $col (@order)
{
push @ret, "<td name=\"$col\" $td_additions>".$line->{$col}."</td>",
}
return join("",@ret)."</tr>";
}
#####################################
# produce a JSON Output for a specific data set depending on visible-columns setting
sub FB_CALLLIST_returnOrderedJSONOutput($$)
{
my ($hash,$line) = @_;
my $name = $hash->{NAME};
my @order = split(",", AttrVal($name, "visible-columns",$hash->{helper}{DEFAULT_COLUMN_ORDER}));
my @ret = ();
push @ret, "\"line\":\"".$line->{line}."\"";
foreach my $col (@order)
{
my $val = $line->{$col};
$val =~ s/"/\\"/g;
push @ret, "\"$col\":\"$val\"",
}
return "{".join(",",@ret)."}";
}
sub FB_CALLLIST_updateReadings($$)
{
my ($hash,$line) = @_;
my $name = $hash->{NAME};
my %line_tmp = %{$line};
my @order = split(",", AttrVal($name, "visible-columns",$hash->{helper}{DEFAULT_COLUMN_ORDER}));
$line_tmp{state} = FB_CALLLIST_returnCallState($hash, $line->{index}, 0);
readingsBeginUpdate($hash);
foreach my $col (@order)
{
readingsBulkUpdate($hash, $line_tmp{line}."-$col", $line_tmp{$col});
}
readingsEndUpdate($hash, 1);
}
#####################################
# Check, if a given internal number matches the configured internal-number-filter (if set). returns true if number matches
sub FB_CALLLIST_checkForInternalNumberFilter($$)
{
my ($hash, $line_number) = @_;
my $name = $hash->{NAME};
if(exists($hash->{helper}{INTERNAL_FILTER}))
{
Log3 $name, 5, "FB_CALLLIST ($name) - internal-number-filter is defined, checking if internal number $line_number is allowed";
if(defined($line_number) and not exists($hash->{helper}{INTERNAL_FILTER}{$line_number}))
{
Log3 $name, 5, "FB_CALLLIST ($name) - internal number $line_number does not match the current internal-number-filter: ".Dumper($hash->{helper}{INTERNAL_FILTER});
return undef;
}
else
{
Log3 $name, 5, "FB_CALLLIST ($name) - call passed the internal-number-filter. proceeding...";
}
}
return 1;
}
#####################################
# update the call list of all connected FHEMWEB clients via inform mechanism
sub FB_CALLLIST_updateFhemWebClients($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
if(exists($hash->{helper}{DATA}) and (scalar keys %{$hash->{helper}{DATA}}) > 0)
{
Log3 $name, 5, "FB_CALLLIST ($name) - inform all FHEMWEB clients";
# inform all FHEMWEB clients about changes
my $count = 0;
foreach my $line (FB_CALLLIST_list2html($hash, 1))
{
FW_directNotify($name, $line, 1);
$count++;
}
# send the current row count to ensure all other rows are deleted via JS
FW_directNotify($name,"max-lines,$count", 1);
}
else
{
Log3 $name, 5, "FB_CALLLIST ($name) - list is empty, sending a clear command to all FHEMWEB clients";
# inform all FHEMWEB clients about empty list
my @columns = split(",",AttrVal($name, "visible-columns", $hash->{helper}{DEFAULT_COLUMN_ORDER}));
my $additional_columns = scalar(@columns);
my $string;
if(AttrVal($name, "language", "en") eq "de")
{
$string = "leer";
}
else
{
$string = "empty";
}
FW_directNotify($name, "clear,$additional_columns,$string", 1);
}
}
sub FB_CALLLIST_returnTableHeader($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $line;
if(AttrVal($name, "language", "en") eq "de")
{
setlocale(LC_ALL, "de_DE.utf8");
$line = {
row => "",
state => "Status",
timestamp => "Zeitpunkt",
name => "Name",
number => "Rufnummer",
internal => "Intern",
external => "Extern",
connection => "Via",
duration => "Dauer"
};
}
else
{
setlocale(LC_ALL, "en_US.utf8");
$line = {
row => "",
state => "State",
timestamp => "Timestamp",
name => "Name",
number => "Number",
internal => "Internal",
external => "External",
connection => "Via",
duration => "Duration"
};
}
return $line;
}
1;
=pod
=begin html
<a name="FB_CALLLIST"></a>
<h3>FB_CALLLIST</h3>
<ul>
<tr><td>
The FB_CALLLIST module creates a call history list by processing events of a <a href="#FB_CALLMONITOR">FB_CALLMONITOR</a> definition.
It logs all calls and displays them in a historic table.
<br><br>
You need a defined FB_CALLMONITOR instance where you can attach FB_CALLLIST to process the call events.<br><br>
Depending on your configuration the status will be shown as icons or as text. You need to have the openautomation icon set configured in your corresponding FHEMWEB instance (see FHEMWEB attribute <a href="#iconPath">iconPath</a>).
<br><br>
The icons have different colors.<br><br>
<ul>
<li><font color="blue"><b>blue</b></font> - incoming call (active or finished)</li>
<li><font color="green"><b>green</b></font> - outgoing call (active or finished)</li>
<li><font color="red"><b>red</b></font> - missed incoming call</li>
</ul>
<br>
If you use no icons (see <a href="#show-icons">show-icons</a>) the following states will be shown:<br><br>
<ul>
<table>
<tr><td><code>&lt;= ((o))</code></td><td> - outgoing call (ringing) - icon: <code>outgoing.ring</code> </td></tr>
<tr><td><code>=&gt; ((o))</code></td><td> - incoming call (ringing) - icon: <code>incoming.ring</code></td></tr><tr><td colspan="2">&nbsp;</td></tr>
<tr><td><code>&lt;= [=]</code></td><td> - outgoing call (currently active) - icon: <code>outgoing.connected</code></td></tr>
<tr><td><code>=&gt; [=]</code></td><td> - incoming call (currently active) - icon: <code>incoming.connected</code></td></tr><tr><td colspan="2">&nbsp;</td></tr>
<tr><td><code>&lt;= X</code></td><td> - outgoing unsuccessful call (nobody picked up) - icon: <code>outgoing.missed</code></td></tr>
<tr><td><code>=&gt; X</code></td><td> - incoming unsuccessful call (missed call) - icon: <code>incoming.missed</code></td></tr><tr><td colspan="2">&nbsp;</td></tr>
<tr><td><code>=&gt; O_O</code></td><td> - incoming finished call recorded on answering machine - icon: <code>incoming.tam</code></td></tr><tr><td colspan="2">&nbsp;</td></tr>
<tr><td><code>&lt;=</code></td><td> - outgoing finished call - icon: <code>outgoing.done</code></td></tr>
<tr><td><code>=&gt;</code></td><td> - incoming finished call - icon: <code>incoming.done</code></td></tr>
</table>
</ul>
<br>
The default icon mapping for all states can be changed by the corresponding attribute.
<br>
<br>
<a name="FB_CALLLISTdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FB_CALLLIST &lt;FB_CALLMONITOR name&gt;</code><br>
</ul>
<br>
<a name="FB_CALLLISTset"></a>
<b>Set</b><br>
<ul>
<li><b>clear</b> - clears the list completely</li>
</ul>
<br>
<a name="FB_CALLLISTget"></a>
<b>Get</b><br>
<ul>
No get commands implemented.
</ul>
<br>
<a name="FB_CALLLISTattr"></a>
<b>Attributes</b><br><br>
<ul>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
<li><a name="disable">disable</a> 0,1</li>
Optional attribute to disable the call list update. When disabled, call events will be processed and the list wouldn't be updated accordingly.
<br><br>
Possible values: 0 => FB_CALLLIST is activated, 1 => FB_CALLLIST is deactivated.<br>
Default Value is 0 (activated)<br><br>
<li><a name="disabledForIntervals">disabledForIntervals</a> HH:MM-HH:MM HH:MM-HH-MM...</li>
Optional attribute to disable the call list update during a specific time interval. The attribute contains a space separated list of HH:MM tupels.
If the current time is between the two time specifications, the callist will be disabled and no longer updated.
Instead of HH:MM you can also specify HH or HH:MM:SS.
<br><br>To specify an interval spawning midnight, you have to specify two intervals, e.g.:
<pre>23:00-24:00 00:00-01:00</pre>
Default Value is <i>empty</i> (no intervals defined, calllist is always active)<br><br>
<li><a name="create-readings">create-readings</a> 0,1</li>
If enabled, for all visible calls in the list, readings and events will be created. It is recommended to set the attribute <a href="#event-on-change-reading">event-on-change-reading</a> to <code>.*</code> (all readings), to reduce the amount of generated readings for certain call events.<br><br>
Possible values: 0 => no readings will be created, 1 => readings and events will be created.<br>
Default Value is 0 (no readings will be created)<br><br>
<li><a name="number-of-calls">number-of-calls</a> 1..20</li>
Defines the maximum number of displayed call entries in the list.<br><br>
Default Value is 5 calls<br><br>
<li><a name="list-type</">list-type</a> all,incoming,outgoing,missed-calls,completed,active</li>
Defines what type of calls should be displayed in the list.<br><br>
Default Value is "all"<br><br>
<li><a name="list-order">list-order</a> descending,ascending</li>
Defines whether the newest call should be on top of the list (descending) or on the bottom of the list (ascending).<br><br>
Default Value is descending (first call at top of the list)<br><br>
<li><a name="internal-number-filter">internal-number-filter</a> &lt;hash&gt;</li>
This attribute accepts a list of comma seperated internal numbers for
filtering incoming or outgoing calls by a specific list of internal numbers
or a hash for filtering and mapping numbers to text.<br>
<br>
e.g.<br>
<ul>
<code>attr &lt;name&gt; internal-number-filter 304050,304060<br><br>
attr &lt;name&gt; internal-number-filter {'304050' =&gt; 'business', '304060' =&gt; 'private'}</code>
</ul>
<br><b>Important:</b> Depending on your provider, the internal number can contain a location area code.
The internal-number-filter must contain the same number as it is displayed in the call list.
This can be with or without location area code depending on your provider.
<br><br>
If this attribute is set, only the configured internal numbers will be shown in the list. All calls which are not taken via the configured internal numbers, were not be shown in the call list.
<br><br>
Default Value: <i>empty</i> (all internal numbers should be used, no exclusions and no mapping is performed)
<br><br>
<li><a name="external-mapping">external-mapping</a> &lt;hash&gt;</li>
Defines a custom mapping of external connection values (reading: external_connection) to custom values. The mapping is performed in a hash table.<br><br>
e.g.<br>
<ul>
<code>attr &lt;name&gt; external-mapping {'ISDN' =&gt; 'Fixed Network', 'SIP0' =&gt; 'Operator A', 'SIP1' =&gt; 'Operator B'}</code>
</ul><br>
<li><a name="icon-mapping">icon-mapping</a> &lt;hash&gt;</li>
Defines a custom mapping of call states to custom icons. The mapping is performed in a hash table.<br><br>
e.g.<br>
<ul>
<code>attr &lt;name&gt; icon-mapping {'incoming.connected' =&gt; 'phone_ring_in@yellow', 'outgoing.missed' =&gt; 'phone_missed_out@red'}</code>
</ul><br>
The mapped name will be displayed in the table instead of the original value from FB_CALLMONITOR. If you use SVG-based icons, you can set the desired color as name or HTML color code via an optional "@<i>color</i>".
<br><br>
Possible values and their default icon are:<br><br>
<ul>
<li><b>incoming.ring</b> =&gt; phone_ring@blue</li>
<li><b>outgoing.ring</b> =&gt; phone_ring@green</li>
<li><b>incoming.connected</b> =&gt; phone_ring_in@blue</li>
<li><b>outgoing.connected</b> =&gt; phone_ring_in@green</li>
<li><b>incoming.missed</b> =&gt; phone_missed_in@red</li>
<li><b>outgoing.missed</b> =&gt; phone_missed_out@green</li>
<li><b>incoming.done</b> =&gt; phone_call_end_in@blue</li>
<li><b>outgoing.done</b> =&gt; phone_call_end_out@green</li>
<li><b>incoming.tam</b> =&gt; phone_answering@blue</li>
</ul>
<br><br>
Default Value: <i>empty</i> (no mapping is performed)
<br><br>
<li><a name="connection-mapping">connection-mapping</a> &lt;hash&gt;</li>
Defines a custom mapping of connection names to custom values. The mapping is performed in a hash table.<br><br>
e.g.<br>
<ul>
<code>attr &lt;name&gt; connection-mapping {'DECT_1' =&gt; 'Mobile Kitchen', 'FON1' =&gt; 'Fax'}</code>
</ul><br>
The mapped name will be displayed in the table instead of the original value from FB_CALLMONITOR.
<br><br>
Default Value: <i>empty</i> (no mapping is performed)
<br><br>
<li><a name="time-format-string">time-format-string</a> &lt;string&gt;</li>
Defines a format string which should be used to format the timestamp values.
It contains several placeholders for different elements of a date/time.
The possible values are standard POSIX strftime() values. Common placeholders are:<br><br>
<ul>
<li><code>%a</code> - The abbreviated weekday name</li>
<li><code>%b</code> - The abbreviated month name</li>
<li><code>%S</code> - The second as a decimal number</li>
<li><code>%M</code> - The minutes as a decimal number</li>
<li><code>%H</code> - The hours as a decimal number</li>
<li><code>%d</code> - The day of the month as a decimal number</li>
<li><code>%m</code> - The month as a decimal number</li>
<li><code>%Y</code> - The year as a decimal number including the century.</li>
</ul><br>
There are further placeholders available.
Please consult the manpage of <code>strftime()</code> or the documentation of your perl interpreter to find out more.
<br><br>
Default value is "%a, %d %b %Y %H:%M:%S" ( = "Sun, 07 Jun 2015 12:50:09")<br><br>
<li><a name="language">language</a> en,de</li>
Defines the language of the table header, some keywords and the timestamp format. You need to have the selected locale installed and available in your operating system.<br><br>
Possible values: en => English , de => German<br>
Default Value is en (English)<br><br>
<li><a name="show-icons">show-icons</a> 0,1</li>
Normally the call state is shown with icons (used from the openautomation icon set).
You need to have openautomation in your iconpath attribute of your appropriate FHEMWEB definition to use this icons.
If you don't want to use icons you can deactivate them with this attribute.<br><br>
Possible values: 0 => no icons , 1 => use icons<br>
Default Value is 1 (use icons)<br><br>
<li><a name="visible-columns">visible-columns</a> row,state,timestamp,name,number,internal,external,connection,duration</li>
Defines the visible columns, as well as the order in which these columns are displayed in the call list (from left to right).
Not all columns must be displayed, you can select only a subset of columns which will be displayed.
<br><br>
The possible values represents the corresponding column.
The column "row" represents the row number within the current list.
<br><br>
Possible values: a combination of <code>row,state,timestamp,name,number,internal,external,connection,duration</code><br>
Default Value is "row,state,timestamp,name,number,internal,external,connection,duration" (show all columns)<br><br>
<li><a name="no-heading">no-heading</a> 0,1</li>
If activated the headline with a link to the detail page of the current definition will be hidden.<br><br>
Possible values: 0 => the heading line will be shown , 1 => the heading line will not be shown<br>
Default Value is 0 (the heading line will be shown)<br><br>
</ul>
<br>
<a name="FB_CALLLISTevents"></a>
<b>Generated Events:</b><br><br>
<ul>
This module generates only readings if the attribute <a href="#create-readings">create-readings</a> is activated. The number and names of the readings depends on the selected columns (see attribute <a href="#visible-columns">visible-columns</a>) and the configured number of calls (see attribute <a href="#number-of-calls">number-of-calls</a>).
</ul>
</ul>
=end html
=begin html_DE
<a name="FB_CALLLIST"></a>
<h3>FB_CALLLIST</h3>
<ul>
<tr><td>
Das FB_CALLLIST Modul erstellt eine Anrufliste f&uuml;r eine konfigurierte <a href="#FB_CALLMONITOR">FB_CALLMONITOR</a> Definition.
Es speichert alle Anrufe und zeigt sie in einer historischen Tabelle an.
<br><br>
Es wird eine bereits konfigurierte FB_CALLMONITOR Definition ben&ouml;tigt, von der FB_CALLLIST die Events entsprechend verarbeiten kann.<br><br>
Abh&auml;ngig von der Konfiguration der Attribute wird der Status als Icon oder als Textzeichen ausgegeben.
Um die Icons korrekt anzeigen zu k&ouml;nnen, muss das openautomation Icon-Set in der entsprechenden FHEMWEB-Instanz konfiguriert sein (siehe dazu FHEMWEB Attribut <a href="#iconPath">iconPath</a>).
<br><br>
Die Icons haben verschiedene Farben:<br><br>
<ul>
<li><font color="blue"><b>blau</b></font> - Eingehender Anruf (aktiv oder beendet)</li>
<li><font color="green"><b>gr&uuml;n</b></font> - Ausgehender Anruf (aktiv oder beendet))</li>
<li><font color="red"><b>rot</b></font> - Verpasster Anruf (eingehend)</li>
</ul>
<br>
Falls keine Icons verwendet werden sollen (siehe Attribut <a href="#show-icons">show-icons</a>), wird der Status wie folgt angezeigt:<br><br>
<ul>
<table>
<tr><td><code>&lt;= ((o))</code></td><td> - Ausgehender Anruf (klingelt)</td></tr>
<tr><td><code>=&gt; ((o))</code></td><td> - Eingehender Anruf (klingelt)</td></tr><tr><td colspan="2">&nbsp;</td></tr>
<tr><td><code>&lt;= [=]</code></td><td> - Ausgehender Anruf (laufendes Gespr&auml;ch)</td></tr>
<tr><td><code>=&gt; [=]</code></td><td> - Eingehender Anruf (laufendes Gespr&auml;ch)</td></tr><tr><td colspan="2">&nbsp;</td></tr>
<tr><td><code>&lt;= X</code></td><td> - Ausgehender, erfolgloser Anruf (Gegenseite nicht abgenommen)</td></tr>
<tr><td><code>=&gt; X</code></td><td> - Eingehender, erfolgloser Anruf (Verpasster Anruf)</td></tr><tr><td colspan="2">&nbsp;</td></tr>
<tr><td><code>=&gt; O_O</code></td><td> - Eingehender Anruf, der durch einen Anrufbeantworter entgegen genommen wurde</td></tr><tr><td colspan="2">&nbsp;</td></tr>
<tr><td><code>&lt;=</code></td><td> - Ausgehender Anruf (beendet)</td></tr>
<tr><td><code>=&gt;</code></td><td> - Eingehender Anruf (beendet)</td></tr>
</table>
</ul>
<br>
<a name="FB_CALLLISTdefine"></a>
<b>Definition</b>
<ul>
<code>define &lt;Name&gt; FB_CALLLIST &lt;FB_CALLMONITOR Name&gt;</code><br>
</ul>
<br>
<a name="FB_CALLLISTset"></a>
<b>Set-Kommandos</b><br>
<ul>
<li><b>clear</b> - l&ouml;scht die gesamte Anrufliste</li>
</ul>
<br>
<a name="FB_CALLLISTget"></a>
<b>Get</b><br>
<ul>
N/A
</ul>
<br>
<a name="FB_CALLLISTattr"></a>
<b>Attributes</b><br><br>
<ul>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
<li><a name="disable">disable</a></li>
Optionales Attribut zur Deaktivierung der Anrufliste. Es werden dann keine Anruf-Events mehr verarbeitet und die Liste nicht weiter aktualisiert.
<br><br>
M&ouml;gliche Werte: 0 => Anrufliste ist aktiv, 1 => Anrufliste ist deaktiviert.<br>
Standardwert ist 0 (aktiv)<br><br>
<li><a name="disableForIntervals">disableForIntervals</a></li>
Optionales Attribut zur Deaktivierung der Anrufliste innerhalb von bestimten Zeitintervallen.
Das Argument ist eine Leerzeichen-getrennte Liste von Minuszeichen-getrennten HH:MM Paaren (Stunde : Minute).
Falls die aktuelle Uhrzeit zwischen diese Werte f&auml;llt, dann wird die Ausf&uuml;hrung, wie beim disable, ausgesetzt.
Statt HH:MM kann man auch HH oder HH:MM:SS angeben.<br><br>
Um einen Intervall um Mitternacht zu spezifizieren, muss man zwei einzelne Intervalle angeben, z.Bsp.:
<pre>23:00-24:00 00:00-01:00</pre>
Standardwert ist <i>nicht gesetzt</i> (aktiv)<br><br>
<li><a name="create-readings">create-readings</a> 0,1</li>
Sofern aktiviert, werden f&uuml;r alle sichtbaren Anrufe in der Liste entsprechende Readings und Events erzeugt.
Es wird empfohlen das Attribut <a href="#event-on-change-reading">event-on-change-reading</a> auf den Wert <code>.*</code> zu stellen um die hohe Anzahl an Events in bestimmten F&auml;llen zu minimieren.<br><br>
M&ouml;gliche Werte: 0 => keine Readings erstellen, 1 => Readings und Events werden erzeugt.<br>
Standardwert ist 0 (keine Readings erstellen)<br><br>
<li><a name="number-of-calls">number-of-calls</a> 1..20</li>
Setzt die maximale Anzahl an Eintr&auml;gen in der Anrufliste. Sollte die Anrufliste voll sein, wird das &auml;lteste Gespr&auml;ch gel&ouml;scht.<br><br>
Standardwert sind 5 Eintr&auml;ge<br><br>
<li><a name="list-type</">list-type</a> all,incoming,outgoing,missed-calls,completed,active</li>
Ist dieses Attribut gesetzt, werden nur bestimmte Typen von Anrufen in der Liste angezeigt:<br><br>
<ul>
<li><code>all</code> - Alle Anrufe werden angezeigt</li>
<li><code>incoming</code> - Alle eingehenden Anrufe werden angezeigt (aktive und abgeschlossene)</li>
<li><code>outgoing</code> - Alle ausgehenden Anrufe werden angezeigt (aktive und abgeschlossene)</li>
<li><code>missed-calls</code> - Alle eingehenden, verpassten Anrufe werden angezeigt.</li>
<li><code>completed</code> - Alle abgeschlossenen Anrufe werden angezeigt (eingehend und ausgehend)</li>
<li><code>active</code> - Alle aktuell laufenden Anrufe werden angezeigt (eingehend und ausgehend)</li>
</ul><br>
Standardwert ist "all" (alle Anrufe anzeigen)<br><br>
<li><a name="list-order">list-order</a> descending,ascending</li>
Gibt an ob der neueste Anruf in der ersten Zeile (aufsteigend =&gt; descending) oder in der letzten Zeile (absteigend =&gt; ascending) in der Liste angezeigt werden soll. Dementsprechend rollt die Liste dann nach oben oder unten durch.<br><br>
Standardwert ist "descending" (absteigend, neuester Anruf in der ersten Zeile)<br><br>
<li><a name="internal-number-filter">internal-number-filter</a> &lt;hash&gt;</li>
Dieses Attribut erm&ouml;glicht das Filtern der angezeigten Anrufe auf bestimmte interne Rufnummern sowie das Zuordnen von Namen zu den internen Rufnummern.<br><br>
Es ist m&ouml;glich eine kommaseparierte Liste an internen Rufnummern anzugeben oder eine Hash-Tabelle in der man den internen Rufnummern eine eigene Bezeichnung zuweist.
<br>
<br>
z.B.<br>
<ul>
<code>attr &lt;name&gt; internal-number-filter 304050,304060<br><br>
attr &lt;name&gt; internal-number-filter {'304050' =&gt; 'geschftl.', '304060' =&gt; 'privat'}</code>
</ul>
<br>
<b>Wichtig:</b> Je nach Telefonanbieter kann der Wert die Ortsvorwahl enthalten. Die Rufnummer muss genauso angegeben werden, wie sie ohne eine Zuordnung in der Anrufliste auftaucht.<br><br>
Wenn dieses Attribut gesetzt ist, werden nur die eingestellten Rufnummern in der Liste angezeigt.
<br><br>
Standardwert ist <i>nicht gesetzt</i> (alle internen Rufnummern werden angezeigt)
<br>
<br>
<li><a name="external-mapping">external-mapping</a> &lt;hash&gt;</li>
Definiert eine eigene Zuordnung der externen Anschlussbezeichnung (Reading: external_connection) zu eigenen Bezeichnungen. Die Zuordnung erfolgt &uuml;ber eine Hash-Struktur.<br><br>
z.B.<br>
<ul>
<code>attr &lt;name&gt; external-mapping {'ISDN' =&gt; 'Festnetz', 'SIP0' =&gt; 'Anbieter A', 'SIP1' => 'Anbieter B'}</code>
</ul><br>
Die jeweils zugeordnete Bezeichnung wird in der Anrufliste dann entsprechend angezeigt anstatt des originalen Werten von FB_CALLMONITOR.
<br><br>
Standardwert ist <i>nicht gesetzt</i> (Keine Zuordnung, es werden die Originalwerte verwendet)
<br><br>
<li><a name="connection-mapping">connection-mapping</a> &lt;hash&gt;</li>
Definiert eine eigene Zuordnung der Endger&auml;te (Reading: internal_connection) zu eigenen Bezeichnungen. Die Zuordnung erfolgt &uuml;ber eine Hash-Struktur.<br><br>
z.B.<br>
<ul>
<code>attr &lt;name&gt; connection-mapping {'DECT_1' =&gt; 'Mobilteil K&uuml;che', 'FON1' =&gt; 'Fax', 'Answering_Machine_1' => 'Anrufbeantworter'}</code>
</ul><br>
Die jeweils zugeordnete Bezeichnung wird in der Anrufliste dann entsprechend angezeigt anstatt des originalen Werten von FB_CALLMONITOR.
<br><br>
Standardwert ist <i>nicht gesetzt</i> (Keine Zuordnung, es werden die Originalwerte verwendet)
<br><br>
<li><a name="icon-mapping">icon-mapping</a> &lt;hash&gt;</li>
Definiert eine eigene Zuordnung eines Anrufstatus zu einem Icon. Die Zuordnung erfolgt &uuml;ber eine Hash-Struktur.<br><br>
z.B.<br>
<ul>
<code>attr &lt;name&gt; icon-mapping {'incoming.connected' =&gt; 'phone_ring_in@yellow', 'outgoing.missed' =&gt; 'phone_missed_out@red'}</code>
</ul><br>
Das entsprechende Icon wird an Stelle des Original-Icons bzw. Text verwendet. Sofern SVG-basierte Icons verwendet werden, kann man die Farbe optional definieren durch das Anfügen via @ mit Name oder einem HTML Farbcode.
<br><br>
M&ouml;gliche Werte und ihre Standard-Icons sind:<br><br>
<ul>
<li><b>incoming.ring</b> =&gt; phone_ring@blue</li>
<li><b>outgoing.ring</b> =&gt; phone_ring@green</li>
<li><b>incoming.connected</b> =&gt; phone_ring_in@blue</li>
<li><b>outgoing.connected</b> =&gt; phone_ring_in@green</li>
<li><b>incoming.missed</b> =&gt; phone_missed_in@red</li>
<li><b>outgoing.missed</b> =&gt; phone_missed_out@green</li>
<li><b>incoming.done</b> =&gt; phone_call_end_in@blue</li>
<li><b>outgoing.done</b> =&gt; phone_call_end_out@green</li>
<li><b>incoming.tam</b> =&gt; phone_answering@blue</li>
</ul>
<br><br>
Standardwert ist <i>nicht gesetzt</i> (Keine Zuordnung, es werden die Standard-Icons verwendet, sofern Icons akitivert sind)
<br><br>
<li><a name="time-format-string">time-format-string</a> &lt;string&gt;</li>
Definiert einen Formatierungs-String welcher benutzt wird um die Zeitangaben in der Anrufliste nach eigenen W&uuml;nschen anzupassen. Es stehen hier eine ganze Reihe an Platzhaltern zur Verf&uuml;gung um die einzelnen Elemente einer Datums-/Zeitangabe einzeln zu setzen. Die m&ouml;glichen Werte sind alle Standard POSIX strftime() Platzhalter. G&auml;ngige Platzhalter sind:<br><br>
<ul>
<li><code>%a</code> - Der abgek&uuml;rzte Wochentagname</li>
<li><code>%b</code> - Der abgek&uuml;rzte Monatsname</li>
<li><code>%S</code> - Die Sekunden als Dezimalzahl</li>
<li><code>%M</code> - Die Minuten als Dezimalzahl</li>
<li><code>%H</code> - Die Stunden als Dezimalzahl</li>
<li><code>%d</code> - Der Tag im Monat als Dezimalzahl</li>
<li><code>%m</code> - Der Monat als Dezimalzahl</li>
<li><code>%Y</code> - Das Jahr als Dezimalzahl (4-stellig).</li>
</ul><br>
Es gibt hierf&uuml;r noch weitere Platzhalter. Weitere Informationen dazu findet man in der Manpage von <code>strftime()</code> oder der Dokumentation des entsprechenden Perl Interpreters.
<br><br>
Standardwert ist "%a, %d %b %Y %H:%M:%S" (entspricht "So, 07 Jun 2015 12:50:09")<br><br>
<li><a name="language">language</a> en,de</li>
Definiert die Sprache in der die Anrufliste angezeigt werden soll (Tabellenkopf, Datum). Die entsprechende Sprache muss auch im Betriebssystem installiert und unterst&uuml;tzt werden.<br><br>
M&ouml;gliche Werte: en => Englisch , de => Deutsch<br>
Standardwert ist en (Englisch)<br><br>
<li><a name="show-icons">show-icons</a> 0,1</li>
Im Normalfall wird der Status eines jeden Anrufs mit einem Icon angezeigt. Dazu muss das openautomation Icon-Set im iconpath-Attribut der entsprechenden FHEMWEB Instanz konfiguriert sein. Sollte man keine Icons w&uuml;nschen, so kann man diese hiermit abschalten. Der Status wird dann mittels Textzeichen dargestellt.<br><br>
M&ouml;gliche Werte: 0 => keine Icons , 1 => benutze Icons<br>
Standardwert ist 1 (benutze Icons)<br><br>
<li><a name="visible-columns">visible-columns</a> row,state,timestamp,name,number,internal,external,connection,duration</li>
Legt fest, welche Spalten in welcher Reihenfolge (von links nach rechts) in der Anrufliste angezeigt werden sollen.
Es m&uuml;ssen nicht alle verf&uuml;gbaren Spalten angezeigt werden.
Es kann auch eine Auswahl von einzelnen Spalten angezeigt werden.
<br><br>
Die m&ouml;glichen Werte repr&auml;sentieren die jeweilige Spalte.
Der Wert "row" steht f&uuml;r die Zeilennummer innerhalb der Liste.
<br><br>
M&ouml;gliche Werte: Eine Kombination der folgenden Werte in der gew&uuml;nschten Reihenfolge: <code>row,state,timestamp,name,number,internal,external,connection,duration</code><br>
Standardwert ist "row,state,timestamp,name,number,internal,external,connection,duration" (Anzeige aller Spalten)<br><br>
<li><a name="no-heading">no-heading</a> 0,1</li>
Sofern aktiviert, wird die &Uuml;berschriftenzeile ausserhalb der Liste inkl. Link auf die Detail-Seite der aktuellen Definition ausgeblendet.<br><br>
M&ouml;gliche Werte: 0 => &Uuml;berschriftenzeile wird angezeigt , 1 => &Uuml;berschriftenzeile wird ausgeblendet<br>
Standardwert ist 1 (&Uuml;berschriftenzeile wird angezeigt)<br><br>
</ul>
<br>
<a name="FB_CALLLISTevents"></a>
<b>Generierte Events:</b><br><br>
<ul>
Dieses Modul generiert Readings/Events sofern das Attribut <a href="#create-readings">create-readings</a> aktiviert ist. Die Anzahl, sowie der Name der Readings ist von den gew&auml;hlten Spalten (Attribut: <a href="#visible-columns">visible-columns</a>), sowie der Anzahl der anzuzeigenden Anrufe abh&auml;ngig (Attribut: <a href="#number-of-calls">number-of-calls</a>).
</ul>
</ul>
=end html_DE
=cut