DoorPi.pm: Aktualisierung auf Version 1.1

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@11847 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
pahenning 2016-07-24 16:39:40 +00:00
parent 0b926d5eda
commit b49e381ab9
5 changed files with 874 additions and 231 deletions

View File

@ -41,7 +41,7 @@ use vars qw{%attr %defs};
sub Log($$);
#-- globals on start
my $version = "1.0beta9";
my $version = "1.1";
#-- these we may get on request
my %gets = (
@ -51,7 +51,7 @@ my %gets = (
);
#-- capabilities of doorpi instance for light and target
my ($lon,$loff,$lonft,$don,$doff,$gtt,$son,$soff,$snon) = (0,0,0,0,0,0,0,0,0);
my ($lon,$loff,$don,$doff,$gtt,$son,$soff,$snon) = (0,0,0,0,0,0,0,0,0);
########################################################################################
#
@ -222,7 +222,12 @@ sub DoorPi_Get ($@) {
sub DoorPi_Set ($@) {
my ($hash, @a) = @_;
#-- only hash as parameter when acting as timer callback
if( !@a ){
@a=($hash->{NAME},"light","off");
}
my $name = shift @a;
my ($newkeys,$key,$value,$v);
#-- commands
@ -268,22 +273,37 @@ sub DoorPi_Set ($@) {
return "[DoorPi_Set] With unknown argument $key, choose one of " . join(" ", @{$hash->{HELPER}->{CMDS}})
if ( !grep( /$key/, @{$hash->{HELPER}->{CMDS}} ) && ($key ne "call") && ($key ne "door") );
#-- hidden command to be used by DoorPi for communicating
#-- hidden command "call" to be used by DoorPi for communicating with this module
if( $key eq "call" ){
if( $value eq "start" ){
if( $value =~ "start.*" ){
readingsSingleUpdate($hash,"call","started",1);
my ($sec, $min, $hour, $day,$month,$year,$wday) = (localtime())[0,1,2,3,4,5,6];
$year += 1900;
my $monthn = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")[$month];
$wday = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")[$wday];
my $timestamp = sprintf("%s, %2d %s %d %02d:%02d:%02d", $wday,$day,$monthn,$year,$hour, $min, $sec);
unshift(@{ $hash->{DATA}}, ["",$timestamp,AttrVal($name, "target$value","unknown"),"active","--","xx","yy"] );
#-- update web interface immediately
DoorPi_inform($hash);
}elsif( $value eq "end" ){
readingsSingleUpdate($hash,"call","ended",1);
readingsSingleUpdate($hash,"call","ended",1);
DoorPi_GetHistory($hash);
#-- update web interface in 5 seconds
InternalTimer(gettimeofday()+5, "DoorPi_inform", $hash,0);
}elsif( $value eq "rejected" ){
readingsSingleUpdate($hash,"call","rejected",1);
DoorPi_GetHistory($hash);
}elsif( $value eq "dismissed" ){
readingsSingleUpdate($hash,"call","dismissed",1);
DoorPi_GetHistory($hash);
}elsif( $value eq "startup" ){
DoorPi_GetConfig($hash);
DoorPi_GetHistory($hash);
}elsif( $value eq "snapshot" ){
# TODO
}else{
@ -364,6 +384,7 @@ sub DoorPi_Set ($@) {
Log 1,"[DoorPi_Set] received softlock command from DoorPi, but uncertain lockstate";
return;
}
#-- from FHEM: unlocking the door
}elsif( $value eq "unlocked" ){
#-- careful here -
@ -378,6 +399,7 @@ sub DoorPi_Set ($@) {
readingsSingleUpdate($hash,"lockstate","unlocked",1);
readingsSingleUpdate($hash,$door,"unlocked",1);
$v=DoorPi_Cmd($hash,"doorunlocked");
#-- from FHEM: locking the door
}elsif( $value eq "locked" ){
#-- careful here -
@ -410,7 +432,6 @@ sub DoorPi_Set ($@) {
#my $light = AttrVal($name, "lightbutton", "light");
if( $value eq "on" ){
$v=DoorPi_Cmd($hash,"lighton");
readingsSingleUpdate($hash,$light,"on",1);
if(AttrVal($name, "lightoncmd",undef)){
fhem(AttrVal($name, "lightoncmd",undef));
}
@ -422,12 +443,13 @@ sub DoorPi_Set ($@) {
}
readingsSingleUpdate($hash,$light,"off",1);
}elsif( $value eq "on-for-timer" ){
$v=DoorPi_Cmd($hash,"lightonfortimer");
if(AttrVal($name, "lighttimercmd",undef)){
fhem(AttrVal($name, "lighttimercmd",undef));
$v=DoorPi_Cmd($hash,"lighton");
if(AttrVal($name, "lightoncmd",undef)){
fhem(AttrVal($name, "lightoncmd",undef));
}
readingsSingleUpdate($hash,$light,"on-for-timer",1);
#-- TODO: reset after time
readingsSingleUpdate($hash,$light,"on",1);
#-- Intiate turning off light
InternalTimer(gettimeofday() + 60, "DoorPi_Set", $hash,1);
}
#-- dashboard lighting
}elsif( $key eq "$dashlight" ){
@ -442,9 +464,12 @@ sub DoorPi_Set ($@) {
}elsif( $key =~ /button(\d\d?)/){
$v=DoorPi_Cmd($hash,$key);
}elsif( $key eq "purge"){
$v=DoorPi_Cmd($hash,"purge");
}elsif( $key eq "clear"){
$v=DoorPi_Cmd($hash,"clear");
#-- command purge to Doorpi
DoorPi_Cmd($hash,"purge");
#-- clearing of DB
InternalTimer(gettimeofday()+5, "DoorPi_PurgeDB", $hash,0);
#-- get new history
InternalTimer(gettimeofday()+10, "DoorPi_GetHistory",$hash,0);
}
if(defined($v)) {
@ -551,8 +576,6 @@ sub DoorPi_GetConfig {
$doff = 1;
#-- check for scene lighting buttons
}elsif($key =~ /$light(on)fortimer/){
$lonft = 1;
}elsif($key =~ /$light(on)/){
push(@{ $hash->{HELPER}->{CMDS}},"$light");
$lon = 1;
@ -585,8 +608,6 @@ sub DoorPi_GetConfig {
if( $lon==0 );
Log 1,"[DoorPi_GetConfig] Warning: No DoorPi InputPin named \"".$light."off\" defined"
if( $loff==0 );
Log 1,"[DoorPi_GetConfig] Warning: No DoorPi InputPin named \"".$light."onfortimer\" defined"
if( $lonft==0 );
Log 1,"[DoorPi_GetConfig] Warning: No DoorPi InputPin named \"".$dashlight."on\" defined"
if( $don==0 );
Log 1,"[DoorPi_GetConfig] Warning: No DoorPi InputPin named \"".$dashlight."off\" defined"
@ -603,6 +624,64 @@ sub DoorPi_GetConfig {
readingsSingleUpdate($hash,"config","ok",1);
return undef;
}
#######################################################################################
#
# DoorPi_LastSnapshot - acts as callable program DoorPi_GetLastSnapshot($hash)
# and as callback program DoorPi_GetLastSnapshot($hash,$err,$status)
#
# Parameter hash, err, status
#
#######################################################################################
sub DoorPi_GetLastSnapshot {
my ($hash,$err,$status) = @_;
my $name = $hash->{NAME};
my $url;
#-- get configuration from doorpi
if ( !$hash ){
Log 1,"[DoorPi_GetLastSnapshot] called without hash";
return undef;
}elsif ( $hash && !$err && !$status ){
$url = "http://".$hash->{TCPIP}."/status?module=config";
#Log 1,"[DoorPi_GetLastSnapshot] called with only hash => Issue a non-blocking call to $url";
HttpUtils_NonblockingGet({
url => $url,
callback => sub($$$){ DoorPi_GetLastSnapshot($hash,$_[1],$_[2]) }
});
return undef;
}elsif ( $hash && $err ){
Log 1,"[DoorPi_GetLastSnapshot] has error $err";
readingsSingleUpdate($hash,"snapshot",$err,0);
readingsSingleUpdate($hash,"state","Error",1);
return;
}
Log 1,"[DoorPi_GetLastSnapshot] has obtained data";
#-- test if this is valid JSON
if( !is_valid_json($status) ){
Log 1,"[DoorPi_GetLastSnapshot] but data is invalid";
readingsSingleUpdate($hash,"snapshot","invalid data",0);
readingsSingleUpdate($hash,"state","Error",1);
return;
}
my $json = JSON->new->utf8;
my $jhash0 = $json->decode( $status );
#-- decode config
my $DoorPi = $jhash0->{"config"}->{"DoorPi"};
my $lastsnap = $jhash0->{"config"}->{"DoorPi"}->{"last_snapshot"};
#push(@{ $hash->{DATA}}, ["",$state,$timestamp,$number,"started","--",$snapshot,$record] );
Log 1,"[DoorPi_GetLastSnapshot] returns $lastsnap";
#-- put into READINGS
readingsSingleUpdate($hash,"snapshot",$lastsnap,1);
return undef;
}
#######################################################################################
#
@ -629,7 +708,7 @@ sub DoorPi_GetHistory {
Log 1,"[DoorPi_GetHistory] called without hash";
return undef;
}elsif ( $hash && !$err1 && !$status1 && !$err2 && !$status2 ){
$url = "http://".$hash->{TCPIP}."/status?module=history_event";
$url = "http://".$hash->{TCPIP}."/status?module=history_event&name=OnCallStateChange&value=1000";
#Log 1,"[DoorPi_GetHistory] called with only hash => Issue a non-blocking call to $url";
HttpUtils_NonblockingGet({
url => $url,
@ -638,7 +717,7 @@ sub DoorPi_GetHistory {
return undef;
}elsif ( $hash && $err1 && !$status1 && !$err2 && !$status2 ){
Log 1,"[DoorPi_GetHistory] has error $err1";
readingsSingleUpdate($hash,"history",$err1,0);
readingsSingleUpdate($hash,"call_history",$err1,0);
readingsSingleUpdate($hash,"state","Error",1);
return undef;
}elsif ( $hash && !$err1 && $status1 && !$err2 && !$status2 ){
@ -651,7 +730,7 @@ sub DoorPi_GetHistory {
return undef;
}elsif ( $hash && !$err1 && $status1 && $err2){
Log 1,"[DoorPi_GetHistory] has error2 $err2";
readingsSingleUpdate($hash,"history",$err2,0);
readingsSingleUpdate($hash,"call_history",$err2,0);
readingsSingleUpdate($hash,"state","Error",1);
return undef;
}
@ -660,13 +739,13 @@ sub DoorPi_GetHistory {
#-- test if this is valid JSON
if( !is_valid_json($status1) ){
Log 1,"[DoorPi_GetHistory] but data from first call is invalid";
readingsSingleUpdate($hash,"history","invalid data 1st call",0);
readingsSingleUpdate($hash,"call_history","invalid data 1st call",0);
readingsSingleUpdate($hash,"state","Error",1);
return;
}
if( !is_valid_json($status2) ){
Log 1,"[DoorPi_GetHistory] but data from second call is invalid";
readingsSingleUpdate($hash,"history","invalid data 2nd call",0);
readingsSingleUpdate($hash,"call_history","invalid data 2nd call",0);
readingsSingleUpdate($hash,"state","Error",1);
return;
}
@ -676,152 +755,171 @@ sub DoorPi_GetHistory {
my $khash0 = $json->decode( $status2 );
#-- decode call history
if(ref($jhash0->{"history_event"}) ne 'ARRAY'){
Log 1,"[DoorPi_GetHistory] Error - has found an empty event history";
return
}
if(ref($khash0->{"history_snapshot"}) ne 'ARRAY'){
Log 1,"[DoorPi_GetHistory] Warning - has found an empty snapshot history";
}
my @history_event = ($jhash0)?@{$jhash0->{"history_event"}}:();
my @history_snapshot = ($khash0)?@{$khash0->{"history_snapshot"}}:();
my $call = "";
#-- clear list of calls
@{$hash->{DATA}} = ();
my ($event,$jhash1,$jhash2,$call_state,$call_state2,$callstart,$callend,$calletime,$calletarget,$callstime,$callstarget,$callsnap,$callrecord,$callstring);
#-- going backward through the calls
my ($callend,$calletime,$calletarget,$callstime,$callstarget,$callsnap,$callrecord,$callstring);
for (my $i=0; $i<@history_event; $i++) {
my $event = $history_event[$i];
if( $event->{"event_name"} eq "OnCallStateChange" ){
my $status1 = $event->{"additional_infos"};
#-- workaround for bug in DoorPi
$status1 =~ tr/'/"/;
my $jhash1 = from_json( $status1 );
my $call_state = $jhash1->{"call_state"};
#-- end of call
if( ($call eq "") && (($call_state == 18) || ($call_state == 13)) ){
$call = "active";
$callrecord = "";
$callend = $jhash1->{"state"};
$callend =~ s/Call //;
if( $callend eq "released" ){
#-- check previous 4 events
for( my $j=1; $j<5; $j++ ){
if( $history_event[$i+$j]->{"event_name"} eq "OnCallStateChange"){
my $status2 = $history_event[$i+$j]->{"additional_infos"};
#-- workaround for bug in DoorPi
$status2 =~ tr/'/"/;
my $jhash2 = from_json( $status2 );
if( $jhash2->{"state"} eq "Busy Here" ){
$callend = "busy";
last;
}elsif( $jhash2->{"state"} eq "Call ended" ){
$callend = "ok";
last;
}
}
}
}elsif( $callend eq "terminated" ){
if( $history_event[$i-1]->{"event_name"} eq "OnSipPhoneCallTimeoutNoResponse"){
$callend = "no response";
}
}
$calletime = $event->{"start_time"};
$calletarget = $jhash1->{"remote_uri"};
}elsif( ($call eq "active") && ($call_state == 2) ){
$call = "";
$callstime = $event->{"start_time"};
$callstarget = $jhash1->{"remote_uri"};
#--
if( $calletarget ne $callstarget){
Log 1,"[DoorPi_GetHistory] Found error in call history of target $calletarget";
}else{
#-- Format values
my $state = "";
my ($sec, $min, $hour, $day,$month,$year,$wday) = (localtime($callstime))[0,1,2,3,4,5,6];
$year += 1900;
my $monthn = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")[$month];
$wday = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")[$wday];
my $timestamp = sprintf("%s, %2d %s %d %02d:%02d:%02d", $wday,$day,$monthn,$year,$hour, $min, $sec);
my $number = $callstarget;
$number =~ s/sip://;
$number =~ s/\@.*//;
my $result = $callend;
my $duration = int(($calletime - $callstime)*10+0.5)/10;
my $record = $callrecord;
$record =~ s/^.*records\///;
#-- workaround for buggy DoorPi
$record = sprintf("%d-%02d-%02d_%02d-%02d-%02d.wav", $year,($month+1),$day,$hour, $min, $sec);
# if( $callend eq "ok");
#-- this is the snapshot file if taken at the same time
my $snapshot = sprintf("%d-%02d-%02d_%02d-%02d-%02d.jpg", $year,($month+1),$day,$hour, $min, $sec);
#-- check if it is present in the list of snapshots
my $found = 0;
for( my $i=0; $i<@history_snapshot; $i++){
if( index($history_snapshot[$i],$snapshot) > -1){
$found = 1;
last;
}
Log 1,"[DoorPi_GetHistory] found ".int(@history_event)." events";
#-- going backward through the calls
my $i=0;
if( int(@history_event) > 0 ){
do{
$event = $history_event[$i];
$calletime = $event->{"start_time"};
$status1 = $event->{"additional_infos"};
#-- workaround for bug in DoorPi
$status1 =~ tr/'/"/;
$jhash1 = from_json( $status1 );
$call_state = $jhash1->{"call_state"};
$calletarget = $jhash1->{"remote_uri"};
my @call_states = ();
push(@call_states,$call_state);
#-- no active call processed and state of call = 18 - or ended = 13
if( ($call eq "") && (($call_state == 18)||($call_state == 13)) ){
$call = "active";
my $j = 1;
#-- check previous max. 5 events
do {
$status2 = $history_event[$i+$j]->{"additional_infos"};
if( $status2 ){
#-- workaround for bug in DoorPi
$status2 =~ tr/'/"/;
$jhash2 = from_json( $status2 );
$call_state2 = $jhash2->{"call_state"};
if( $call_state2 < 18 ){
push( @call_states,$call_state2);
$callstime = $history_event[$i+$j]->{"start_time"};
$callstarget = $jhash2->{"remote_uri"};
}
#-- if not, look for a file made a second later
if( $found == 0 ){
($sec, $min, $hour, $day,$month,$year,$wday) = (localtime($callstime+1))[0,1,2,3,4,5,6];
$year += 1900;
$monthn = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")[$month];
$wday = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")[$wday];
#-- this is the snapshot file if taken at the same time
$snapshot = sprintf("%d-%02d-%02d_%02d-%02d-%02d.jpg", $year,($month+1),$day,$hour, $min, $sec);
#-- check if it is present in the list of snapshots
$found = 0;
for( my $i=0; $i<@history_snapshot; $i++){
if( index($history_snapshot[$i],$snapshot) > -1){
$found = 1;
last;
}
}
if( $found == 0 ){
Log 1,"[DoorPi_GetHistory] No snapshot found with $snapshot";
}
}
#-- store this
push(@{ $hash->{DATA}}, [$state,$timestamp,$number,$result,$duration,$snapshot,$record] );
}
}
$j++;
} until( ($j > 5) || ($call_state2 == 18) || ($i+$j >= int(@history_event)) );
my $call_pattern = join("-",@call_states);
#Log 1,"[DoorPi_GetHistory] Pattern for call is $call_pattern, proceeding with event no. ".($i+$j);
if( $call_pattern =~ /1(3|8)\-.*\-2/ ){
$callend = "ok(2)";
}elsif( $call_pattern =~ /1(3|8)\-.*\-3/ ){
$callend = "ok(3)";
}elsif( $call_pattern =~ /1(3|8)\-.*\-5/ ){
$callend = "nok(5)";
}else{
$callend = "unknown";
}
if( $calletarget ne $callstarget){
Log 1,"[DoorPi_GetHistory] Found error in call history of target $calletarget";
}
#-- Format values
my $state = "";
my ($sec, $min, $hour, $day,$month,$year,$wday) = (localtime($callstime))[0,1,2,3,4,5,6];
$year += 1900;
my $monthn = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")[$month];
$wday = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")[$wday];
my $timestamp = sprintf("%s, %2d %s %d %02d:%02d:%02d", $wday,$day,$monthn,$year,$hour, $min, $sec);
my $number = $callstarget;
$number =~ s/sip://;
$number =~ s/\@.*//;
my $result = $callend;
my $duration = int(($calletime - $callstime)*10+0.5)/10;
#-- workaround for buggy DoorPi
my $record = sprintf("%d-%02d-%02d_%02d-%02d-%02d.wav", $year,($month+1),$day,$hour, $min, $sec);
#-- this is the snapshot file if taken at the same time
my $snapshot = sprintf("%d-%02d-%02d_%02d-%02d-%02d.jpg", $year,($month+1),$day,$hour, $min, $sec);
#-- maybe we have to look at a second later ?
($sec, $min, $hour, $day,$month,$year,$wday) = (localtime($callstime+1))[0,1,2,3,4,5,6];
$year += 1900;
$monthn = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")[$month];
$wday = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")[$wday];
#-- this is the filename without extension if taken a second later
my $later = sprintf("%d-%02d-%02d_%02d-%02d-%02d", $year,($month+1),$day,$hour, $min, $sec);
my $found = 0;
for( my $i=0; $i<@history_snapshot; $i++){
if( index($history_snapshot[$i],$snapshot) > -1){
$found = 1;
last;
}
}
#-- if not, look for a file made a second later
if( $found == 0 ){
#-- this is the snapshot file if taken a second later
$snapshot = sprintf("%s.jpg", $later);
#-- check if it is present in the list of snapshots
for( my $i=0; $i<@history_snapshot; $i++){
if( index($history_snapshot[$i],$snapshot) > -1){
$found = 1;
last;
}
}
if( $found == 0 ){
Log 1,"[DoorPi_GetHistory] No snapshot found with $snapshot";
}
}
$found = 0;
for( my $i=0; $i<@history_snapshot; $i++){
if( index($history_snapshot[$i],$record) > -1){
$found = 1;
last;
}
}
#-- if not, look for a file made a second later
if( $found == 0 ){
#-- this is the record file if taken a second later
$record = sprintf("%s.wav", $later);
#-- check if it is present in the list of snapshots
for( my $i=0; $i<@history_snapshot; $i++){
if( index($history_snapshot[$i],$record) > -1){
$found = 1;
last;
}
}
if( $found == 0 ){
Log 1,"[DoorPi_GetHistory] No record found with $record";
}
}
#Log 1,"$snapshot $record";
#-- store this
push(@{ $hash->{DATA}}, [$state,$timestamp,$number,$result,$duration,$snapshot,$record] );
$i += $j-1;
$i--
if( $call_state2 == 18 );
$call = "";
}
#-- other events during call active
if( ($call eq "active") && ($event->{"event_name"} eq "OnRecorderStarted") ){
my $status3 = $event->{"additional_infos"};
$status3 =~ tr/'/"/;
my $jhash1 = from_json( $status3 );
$callrecord = $jhash1->{"last_record_filename"};
}
}
$i++;
} until ($i >= int(@history_event));
#-- going backward through the events to find last action for dashlight and light
my $dashlightstate = "off";
my $dashlight = AttrVal($name, "dashlightbutton", "dashlight");
for (my $i=0; $i<@history_event; $i++) {
if( $history_event[$i]->{"event_name"} =~ /OnKeyPressed_webservice\.dashlight(.*)/ ){
$dashlightstate=$1;
last;
}
}
my $lightstate = "off";
my $light = AttrVal($name, "lightbutton", "light");
for (my $i=0; $i<@history_event; $i++) {
if( $history_event[$i]->{"event_name"} =~ /OnKeyPressed_webservice\.light(.*)/ ){
$lightstate=$1;
last;
}
}
#--put into READINGS
readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"call_listed",int(@{ $hash->{DATA}}));
readingsBulkUpdate($hash,"call_history","ok");
readingsBulkUpdate($hash,$dashlight,$dashlightstate);
readingsBulkUpdate($hash,$light,$lightstate);
#readingsBulkUpdate($hash,$dashlight,$dashlightstate);
#readingsBulkUpdate($hash,$light,$lightstate);
readingsEndUpdate($hash,1);
return undef;
}
@ -881,6 +979,54 @@ sub DoorPi_GetHistory {
return undef;
}
######################################################################################
#
# DoorPi_PurgeDB - acts as callable program DoorPi_PurgeDB($hash)
# and as callback program DoorPi_PurgeDB($hash,$err,$status)
#
# Parameter hash, err, status
#
#######################################################################################
sub DoorPi_PurgeDB {
my ($hash,$err,$data) = @_;
my $name = $hash->{NAME};
my $url;
#-- purge doorpi database
if ( !$hash ){
Log 1,"[DoorPi_PurgeDB] called without hash";
return undef;
}elsif ( $hash && !$err ){
$url = "http://".$hash->{TCPIP}."/status?module=history_event&name=purge&value=1.0";
HttpUtils_NonblockingGet({
url => $url,
callback => sub($$$){ DoorPi_PurgeDB($hash,$_[1],$_[2]) }
});
return undef;
}elsif ( $hash && $err ){
Log 1,"[DoorPi_PurgeDB] has error $err";
readingsSingleUpdate($hash,"config",$err,0);
readingsSingleUpdate($hash,"state","Error",1);
return;
}
#-- test if this is valid JSON
if( !is_valid_json($data) ){
Log 1,"[DoorPi_PurgeDB] invalid data";
readingsSingleUpdate($hash,"state","Error",1);
return;
}
my $json = JSON->new->utf8;
my $jhash = $json->decode( $data );
my $msg = $jhash->{'message'};
my $suc = $jhash->{'success'};
if( $suc ){
return $msg;
}
return undef;
}
#######################################################################################
#
# DoorPi_makeShort
@ -921,7 +1067,7 @@ sub DoorPi_makeShort($$$$){
$ret .= "</td><td>".int(@{ $hash->{DATA}})." calls";
}
$ret .= "</td><td><a href=\"/fhem?cmd.A.Haus.T=set A.Haus.T open\">open</a>";
$ret .= "</td><td><a href=\"/fhem?cmd.$devname=set $devname door open\">open</a>";
setlocale(LC_ALL, $old_locale);
@ -939,15 +1085,58 @@ sub DoorPi_makeShort($$$$){
sub DoorPi_makeTable($$$$){
my ($FW_wname, $devname, $room, $extPage) = @_;
my $hash = $defs{$devname};
return DoorPi_list($hash)
}
#######################################################################################
#
# DoorPi_inform
#
# Inform FHEMWEB
#
#######################################################################################
sub DoorPi_inform($){
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "[Doorpi_inform]- inform all FHEMWEB clients";
my $count = 0;
foreach my $line (DoorPi_list($hash,1)){
#Log 1,"[Doorpi_Set] - informing $name with $line";
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);
}
#######################################################################################
#
# DoorPi_list
#
# Do the work for makeTable
#
#######################################################################################
sub DoorPi_list($;$){
my ($hash, $to_json) = @_;
return undef if( !$hash );
my $name = $hash->{NAME};
my $wwwpath = $hash->{HELPER}->{wwwpath};
my $alias = AttrVal($hash->{NAME}, "alias", $hash->{NAME});
my ($state,$timestamp,$number,$result,$duration,$snapshot,$record,$nrecord);
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 @json_output = ();
my $line;
my $old_locale = setlocale(LC_ALL);
@ -976,9 +1165,11 @@ sub DoorPi_makeTable($$$$){
$ret .= "<tr><td></td><td>";
#-- div tag to support inform updates
$ret .= '<div class="fhemWidget" informId="'.$name.'" cmd="" arg="fbcalllist" dev="'.$name.'">';
if( exists($hash->{DATA}) && (int(@{$hash->{DATA}}) > 0) ){
$ret .= '<div class="fhemWidget" informId="'.$name.'" cmd="" arg="doorpicalllist" dev="'.$name.'">';
if( exists($hash->{DATA}) ){
$ret .= '<table class="block doorpicalllist">';
my @order=("state","timestamp","number","result","duration","record");
if(AttrVal($name, "language", "en") eq "de"){
$state = "Wer";
@ -1004,56 +1195,85 @@ sub DoorPi_makeTable($$$$){
$ret .= '<td name="record" class="doorpicalllist" '.$td_style.'>'.$record.'</td>';
$ret .= '</tr>';
my @list = @{$hash->{DATA}};
for(my $index=0; $index<(@list); $index++){
my @data = @{$list[$index]};
$state = $data[0];
$timestamp = $data[1];
$number = $data[2];
$result = $data[3];
$duration = $data[4];
$snapshot = $data[5];
$record = $data[6];
#-- Loop through all entries in the list
if( int(@{$hash->{DATA}}) > 0){
my @list = @{$hash->{DATA}};
for(my $index=0; $index<(@list); $index++){
my @data = @{$list[$index]};
$state = $data[0];
$timestamp = $data[1];
$number = $data[2];
$result = $data[3];
$duration = $data[4];
$snapshot = $data[5];
$record = $data[6];
if(AttrVal($name, "language", "en") eq "de"){
$result =~ s/busy/besetzt/;
$result =~ s/no\sresponse/ohne Antw./;
if(AttrVal($name, "language", "en") eq "de"){
$result =~ s/busy/besetzt/;
$result =~ s/no\sresponse/ohne Antw./;
}
if( $record ne ""){
my $rs = $record;
$rs =~ s/.*$wwwpath\///;
$record = '<a href="http://'.$hash->{TCPIP}.'/'.$record.'">';
$record .= ($iconaudio) ? $iconaudio : $rs;
$record .= '</a>';
}
if( $snapshot ne ""){
$state = '<a href="http://'.$hash->{TCPIP}.'/'.$snapshot.'">';
$state .= ($iconpic) ? $iconpic : '<img src="http://'.$hash->{TCPIP}.'/'.$snapshot.'" width="40" height="30">';
$state .= '</a>';
}
#-- assemble line
my $line = {
index => $index,
line => $index+1,
state => $state,
timestamp => $timestamp,
number => $number,
result => $result,
duration => $duration,
snapshot => $snapshot,
record => $record
};
#-- assemble HTML output
my @htmlret = ();
push @htmlret, '<tr align="center" number="'.$index.'" class="doorpicalllist '.($index % 2 == 1 ? "odd" : "even").'">';
foreach my $col (@order){
push @htmlret, '<td name="'.$col.'" class="doorpicalllist" '.$td_style.'>'.$line->{$col}.'</td>';
}
$ret .= join("",@htmlret)."</tr>";
#-- assemble JSON output
my @jsonret = ();
push @jsonret, '"line":"'.$line->{index}.'"';
foreach my $col (@order){
my $val = $line->{$col};
$val =~ s,",\\",g;
push @jsonret, '"'.$col.'":"'.$val.'"';
}
push @json_output, "{".join(",",@jsonret)."}";
#--- end loop through the list
}
if( $record ne ""){
my $rs = $record;
$rs =~ s/.*$wwwpath\///;
$rs = ($iconaudio) ? $iconaudio : $rs;
$record = '<a href="http://'.$hash->{TCPIP}.'/'.$record.'">'.$rs.'</a>';
}
if( $snapshot ne ""){
$state = '<a href="http://'.$hash->{TCPIP}.'/'.$snapshot.'">';
$state .= ($iconpic) ? $iconpic : '<img src="http://'.$hash->{TCPIP}.'/'.$snapshot.'" width="40" height="30"></a>';
}
$ret .= '<tr align="center" class="doorpicalllist '.($index % 2 == 1 ? "odd" : "even").'">';
$ret .= '<td name="state" class="doorpicalllist" '.$td_style.'>'.$state.'</td>';
$ret .= '<td name="timestamp" class="doorpicalllist" '.$td_style.'>'.$timestamp.'</td>';
$ret .= '<td name="number" class="doorpicalllist" '.$td_style.'>'.$number.'</td>';
$ret .= '<td name="result" class="doorpicalllist" '.$td_style.'>'.$result.'</td>';
$ret .= '<td name="duration" class="doorpicalllist" '.$td_style.'>'.$duration.'</td>';
$ret .= '<td name="record" class="doorpicalllist" '.$td_style.'>'.$record.'</td>';
$ret .= '</tr>';
}
$ret .= "</table></div>";
}else{
if(AttrVal($name, "language", "en") eq "de"){
$ret .= "</td><td>Rufliste leer";
}else{
$ret .= "</td><td>Calllist empty";
}
if(AttrVal($name, "language", "en") eq "de"){
$ret .= "</td><td>Rufliste leer";
}else{
$ret .= "</td><td>Calllist empty";
}
}
$ret .= "</table></div>";
}
$ret .= "</td></tr></table>";
setlocale(LC_ALL, $old_locale);
return ($ret);
return ($to_json ? @json_output : $ret);
#return ($ret);
}
@ -1124,10 +1344,7 @@ sub DoorPi_makeTable($$$$){
Activate one of the virtual buttons specified in DoorPi.</li>
<li><a name="doorpi_purge">
<code>set &lt;DoorPi-Device&gt; purge </code></a><br />
Clean all recordings and snapshots which are older than the current process </li>
<li><a name="doorpi_clear">
<code>set &lt;DoorPi-Device&gt; clear </code></a><br />
Clear all recordings and snapshots </li>
Clear all recordings and snapshots which are older than a day</li>
</ul>
<br />
<a name="DoorPi_Get"></a>
@ -1175,9 +1392,6 @@ sub DoorPi_makeTable($$$$){
<li><a name="doorpi_lightoffcmd"><code>attr &lt;DoorPi-Device&gt; lightoffcmd
&lt;string&gt;</code></a>
<br />FHEM command additionally executed for "light off" action (no default)</li>
<li><a name="doorpi_lighttimercmd"><code>attr &lt;DoorPi-Device&gt; lighttimercmd
&lt;string&gt;</code></a>
<br />FHEM command additionally executed for "light off" action (no default)</li>
<li><a name="doorpi_dashlightbutton"><code>attr &lt;DoorPi-Device&gt; dashlightbutton
&lt;string&gt;</code></a>
<br />DoorPi name for dashlight action (default: dashlight)</li>
@ -1222,14 +1436,14 @@ base_path_output = &lt;some directory&gt;
dooropen = &lt;doorpi action opening the door&gt;
doorlocked = &lt;doorpi action if the door is locked by FHEM&gt;
doorunlocked = &lt;doorpi action if the door is unlocked by FHEM&gt;
streamon = &lt;doorpi action to switch on video stream&gt;
streamoff = &lt;doorpi action to switch off video stream&gt;
lighton = &lt;doorpi action to switch on scene light&gt;
lightonfortimer = &lt;doorpi action to switch on scene light for some time&gt;
lightoff = &lt;doorpi action to switch off scene light&gt;
dashlighton = &lt;doorpi action to switch on dashlight&gt;
dashlightoff = &lt;doorpi action to switch off dashlight&gt;
gettarget = &lt;doorpi action to acquire call target number&gt;
purge = &lt;doorpi action to purge old files&gt;
clear = &lt;doorpi action&gt;
purge = &lt;doorpi action to purge files and entries older than a day&gt;
... (optional buttons)
button1 = &lt;some doorpi action&gt;
... (further button definitions)

View File

@ -11,10 +11,10 @@ checkstream() {
}
FHEMDP="A.Door.Pi"
FHEMIP="xxx"
FHEM="http://$FHEMIP:8083/fhem?XHR=1&cmd.$FHEMDP"
FHEMIP="192.168.xxx"
FHEM="http://192.168xx:8083/fhem?XHR=1&cmd.$FHEMDP"
HOME="/home/doorpi"
default_target=""
default_target="722xxxx"
case $1 in
@ -52,10 +52,15 @@ case $1 in
;;
purge)
find $HOME/records/ -type f ! -newer /var/run/doorpi.pid -delete
find $HOME/records/ -type f -ctime 1 -delete
;;
clear)
movement)
curl "$FHEM=set%20$FHEMDP%20door%20movement" &
;;
sabotage)
curl "$FHEM=set%20$FHEMDP%20door%20sabotage" &
;;
esac

View File

@ -61,10 +61,10 @@ local_port = 5060
firewallpolicy = PolicyNoFirewall
#
sipphonetyp = linphone
sipserver_password = xxxx
sipserver_password = xxxxxxxxx
sipserver_realm = fritz.box
sipserver_server = xxxx
sipserver_username = 620
sipserver_server = xxxxxxx
sipserver_username = xxxxxx
stun_server =
#
max_call_time = 300
@ -93,8 +93,10 @@ video_size = vga
20 = os_execute:/home/doorpi/FHEMHelper.sh call init
[EVENT_BeforeSipPhoneMakeCall]
10 = os_execute:/home/doorpi/FHEMHelper.sh call startup
20 = take_snapshot
10 = out:irlight,1
20 = os_execute:/home/doorpi/FHEMHelper.sh call startup
30 = take_snapshot
40 = out:irlight,0
#30 = mailto:haus271828@henning-weingarten.de,DoorPi,DoorPi initiating call
[EVENT_OnCallStateDisconnect]
@ -118,6 +120,7 @@ onboardpins = piface
[webservice_keyboard]
base_path_input = /home/doorpi/keyboard/inputs/
base_path_output = /home/doorpi/keyboard/outputs/
reset_input=false
[webservice_InputPins]
dooropen = out:door,1,0,3
@ -127,7 +130,7 @@ snapshot = sleep:0
streamon = sleep:0
streamoff = sleep:0
lighton = out:light,1
lightonfortimer = out:light,1,0,60
#lightonfortimer = out:light,1,0,60
lightoff = out:light,0
dashlighton = out:dashlight,1
dashlightoff = out:dashlight,0
@ -139,8 +142,10 @@ button2 = sleep:0
#-- communicate to FHEM that a snapshot has been taken
[EVENT_OnKeyPressed_webservice.snapshot]
10 = take_snapshot
10 = out:irlight,1
20 = os_execute:/home/doorpi/FHEMHelper.sh call snapshot
30 = take_snapshot
40 = out:irlight,0
#-- start video stream
[EVENT_OnKeyPressed_webservice.streamon]
@ -176,7 +181,8 @@ pull_up_down = PUD_UP
0 = door
1 = light
2 = dashlight
3 = hardlock
3 = irlight
4 = hardlock
7 = blinking_led
[onboardpins_InputPins]
@ -187,19 +193,18 @@ pull_up_down = PUD_UP
6 = sleep:0
7 = sleep:0
#-- DoorOpen pin from Arduino
#-- DoorOpen pin from Arduino
[EVENT_OnKeyPressed_onboardpins.1]
10 = out:dashlight,1
20 = os_execute:/home/doorpi/FHEMHelper.sh doorunlockandopen
30 = os_execute:aplay -D plughw:1,0 /home/doorpi/sounds/067_willkommen.wav
40 = out:dashlight,0
10 = os_execute:/home/doorpi/FHEMHelper.sh doorunlockandopen
20 = os_execute:aplay -D plughw:1,0 /home/doorpi/sounds/067_willkommen.wav
#-- WrongID pin from Arduino
[EVENT_OnKeyPressed_onboardpins.4]
10 = out:dashlight,1
10 = out:irlight,1
20 = os_execute:/home/doorpi/FHEMHelper.sh wrongid
30 = take_snapshot
40 = out:dashlight,0
40 = out:irlight,0
#-- LockState pin from Arduino - FHEM will transform softlock into hardlock
[EVENT_OnKeyPressed_onboardpins.5]
@ -207,8 +212,9 @@ pull_up_down = PUD_UP
#-- Movement detection
[EVENT_OnKeyPressed_onboardpins.6]
10 = out:dashlight,1,0,60
10 = out:dashlight,1,0,1
20 = os_execute:/home/doorpi/FHEMHelper.sh movement
30 = sleep:60
#-- Sabotage detection
[EVENT_OnKeyPressed_onboardpins.7]

385
contrib/DoorPi/handler.py Normal file
View File

@ -0,0 +1,385 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
logger = logging.getLogger(__name__)
logger.debug("%s loaded", __name__)
import threading
import time # used by: fire_event_synchron
from inspect import isfunction, ismethod # used by: register_action
import string, random # used by event_id
import sqlite3
import os
from base import SingleAction
import doorpi
class EnumWaitSignalsClass():
WaitToFinish = True
WaitToEnd = True
sync = True
syncron = True
DontWaitToFinish = False
DontWaitToEnd = False
async = False
asyncron = False
EnumWaitSignals = EnumWaitSignalsClass()
ONTIME = 'OnTime'
def id_generator(size = 6, chars = string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
class EventLog(object):
_db = False
#doorpi.DoorPi().conf.get_string_parsed('DoorPi', 'eventlog', '!BASEPATH!/conf/eventlog.db')
def __init__(self, file_name):
if not file_name: return
try:
if not os.path.exists(os.path.dirname(file_name)):
logger.info('Path %s does not exist - creating it now', os.path.dirname(file_name))
os.makedirs(os.path.dirname(file_name))
#https://docs.python.org/2/library/sqlite3.html#sqlite3.connect
self._db = sqlite3.connect(
database = file_name,
timeout = 1,
check_same_thread = False
)
self.execute_sql('''
CREATE TABLE IF NOT EXISTS event_log (
event_id TEXT,
fired_by TEXT,
event_name TEXT,
start_time REAL,
additional_infos TEXT
);'''
)
self.execute_sql('''
CREATE TABLE IF NOT EXISTS action_log (
event_id TEXT,
action_name TEXT,
start_time REAL,
action_result TEXT
);'''
)
except:
logger.error('error to create event_db')
#-- CODE by PAH
def purge_logs(self, period = 1):
try:
limit=int(time.time()-period*86400)
logger.info('purge event and action log older than %s days => limit = %s',period,limit)
sql_statement = '''
DELETE FROM event_log
WHERE (start_time < '{limit}');'''.format(limit = limit)
self.execute_sql(sql_statement)
sql_statement = '''
DELETE FROM action_log
WHERE (start_time < '{limit}');'''.format(limit = limit)
self.execute_sql(sql_statement)
self._db.commit()
return "0"
except Exception as exp:
logger.exception(exp)
return "-1"
#-- END CODE by PAH
def get_event_log_entries_count(self, filter = ''):
logger.debug('request event logs count with filter %s', filter)
try:
return self.execute_sql('''
SELECT COUNT(*)
FROM event_log
WHERE event_id LIKE '%{filter}%'
OR fired_by LIKE '%{filter}%'
OR event_name LIKE '%{filter}%'
OR start_time LIKE '%{filter}%'
'''.format(filter = filter)).fetchone()[0]
except Exception as exp:
logger.exception(exp)
return "-1"
def get_event_log_entries(self, max_count = 100, filter = ''):
logger.debug('request last %s event logs with filter %s', max_count, filter)
return_object = []
sql_statement = '''
SELECT
event_id,
fired_by,
event_name,
start_time,
additional_infos
FROM event_log
WHERE event_id LIKE '%{filter}%'
OR fired_by LIKE '%{filter}%'
OR event_name LIKE '%{filter}%'
OR start_time LIKE '%{filter}%'
ORDER BY start_time DESC
LIMIT {max_count}'''.format(max_count = max_count, filter = filter)
for single_row in self.execute_sql(sql_statement):
return_object.append({
'event_id': single_row[0],
'fired_by': single_row[1],
'event_name': single_row[2],
'start_time': single_row[3],
'additional_infos': single_row[4]
})
return return_object
def execute_sql(self, sql):
if not self._db: return
#logger.trace('fire sql: %s', sql)
return self._db.execute(sql)
def insert_event_log(self, event_id, fired_by, event_name, start_time, additional_infos):
sql_statement = '''
INSERT INTO event_log VALUES (
"{event_id}","{fired_by}","{event_name}",{start_time},"{additional_infos}"
);
'''.format(
event_id = event_id,
fired_by = fired_by.replace('"', "'"),
event_name = event_name.replace('"', "'"),
start_time = start_time,
additional_infos = str(additional_infos).replace('"', "'")
)
self.execute_sql(sql_statement)
#-- CODE by PAH
try: self._db.commit()
except: pass
def insert_action_log(self, event_id, action_name, start_time, action_result):
sql_statement = '''
INSERT INTO action_log VALUES (
"{event_id}","{action_name}",{start_time},"{action_result}"
);
'''.format(
event_id = event_id,
action_name = action_name.replace('"', "'"),
start_time = start_time,
action_result = str(action_result).replace('"', "'")
)
self.execute_sql(sql_statement)
#-- CODE by PAH
#try: self._db.commit()
#except: pass
def update_event_log(self):
pass
def destroy(self):
try: self._db.close()
except: pass
__del__ = destroy
class EventHandler:
__Sources = [] # Auflistung Sources
__Events = {} # Zuordnung Event zu Sources (1 : n)
__Actions = {} # Zuordnung Event zu Actions (1: n)
__additional_informations = {}
@property
def event_history(self): return self.db.get_event_log_entries()
@property
def sources(self): return self.__Sources
@property
def events(self): return self.__Events
@property
def events_by_source(self):
events_by_source = {}
for event in self.events:
for source in self.events[event]:
if source in events_by_source:
events_by_source[source].append(event)
else:
events_by_source[source] = [event]
return events_by_source
@property
def actions(self): return self.__Actions
@property
def threads(self): return threading.enumerate()
@property
def idle(self): return len(self.threads) - 1 is 0
@property
def additional_informations(self): return self.__additional_informations
def __init__(self):
db_path = doorpi.DoorPi().config.get_string_parsed('DoorPi', 'eventlog', '!BASEPATH!/conf/eventlog.db')
self.db = EventLog(db_path)
__destroy = False
def destroy(self, force_destroy = False):
self.__destroy = True
self.db.destroy()
def register_source(self, event_source):
if event_source not in self.__Sources:
self.__Sources.append(event_source)
logger.debug("event_source %s was added", event_source)
def register_event(self, event_name, event_source):
silent = ONTIME in event_name
if not silent: logger.trace("register Event %s from %s ", event_name, event_source)
self.register_source(event_source)
if event_name not in self.__Events:
self.__Events[event_name] = [event_source]
if not silent: logger.trace("added event_name %s and registered source %s", event_name, event_source)
elif event_source not in self.__Events[event_name]:
self.__Events[event_name].append(event_source)
if not silent: logger.trace("added event_source %s to existing event %s", event_source, event_name)
else:
if not silent: logger.trace("nothing to do - event %s from source %s is already known", event_name, event_source)
def fire_event(self, event_name, event_source, syncron = False, kwargs = None):
if syncron is False: return self.fire_event_asynchron(event_name, event_source, kwargs)
else: return self.fire_event_synchron(event_name, event_source, kwargs)
def fire_event_asynchron(self, event_name, event_source, kwargs = None):
silent = ONTIME in event_name
if self.__destroy and not silent: return False
if not silent: logger.trace("fire Event %s from %s asyncron", event_name, event_source)
return threading.Thread(
target = self.fire_event_synchron,
args = (event_name, event_source, kwargs),
name = "%s from %s" % (event_name, event_source)
).start()
def fire_event_asynchron_daemon(self, event_name, event_source, kwargs = None):
logger.trace("fire Event %s from %s asyncron and as daemons", event_name, event_source)
t = threading.Thread(
target = self.fire_event_synchron,
args = (event_name, event_source, kwargs),
name = "daemon %s from %s" % (event_name, event_source)
)
t.daemon = True
t.start()
def fire_event_synchron(self, event_name, event_source, kwargs = None):
silent = ONTIME in event_name
if self.__destroy and not silent: return False
event_fire_id = id_generator()
start_time = time.time()
if not silent: self.db.insert_event_log(event_fire_id, event_source, event_name, start_time, kwargs)
if event_source not in self.__Sources:
logger.warning('source %s unknown - skip fire_event %s', event_source, event_name)
return "source unknown"
if event_name not in self.__Events:
logger.warning('event %s unknown - skip fire_event %s from %s', event_name, event_name, event_source)
return "event unknown"
if event_source not in self.__Events[event_name]:
logger.warning('source %s unknown for this event - skip fire_event %s from %s', event_name, event_name, event_source)
return "source unknown for this event"
if event_name not in self.__Actions:
if not silent: logger.debug('no actions for event %s - skip fire_event %s from %s', event_name, event_name, event_source)
return "no actions for this event"
if kwargs is None: kwargs = {}
kwargs.update({
'last_fired': str(start_time),
'last_fired_from': event_source,
'event_fire_id': event_fire_id
})
self.__additional_informations[event_name] = kwargs
if 'last_finished' not in self.__additional_informations[event_name]:
self.__additional_informations[event_name]['last_finished'] = None
if 'last_duration' not in self.__additional_informations[event_name]:
self.__additional_informations[event_name]['last_duration'] = None
if not silent: logger.debug("[%s] fire for event %s this actions %s ", event_fire_id, event_name, self.__Actions[event_name])
for action in self.__Actions[event_name]:
if not silent: logger.trace("[%s] try to fire action %s", event_fire_id, action)
try:
result = action.run(silent)
if not silent: self.db.insert_action_log(event_fire_id, action.name, start_time, result)
if action.single_fire_action is True: del action
except SystemExit as exp:
logger.info('[%s] Detected SystemExit and shutdown DoorPi (Message: %s)', event_fire_id, exp)
doorpi.DoorPi().destroy()
except KeyboardInterrupt as exp:
logger.info("[%s] Detected KeyboardInterrupt and shutdown DoorPi (Message: %s)", event_fire_id, exp)
doorpi.DoorPi().destroy()
except:
logger.exception("[%s] error while fire action %s for event_name %s", event_fire_id, action, event_name)
if not silent: logger.trace("[%s] finished fire_event for event_name %s", event_fire_id, event_name)
self.__additional_informations[event_name]['last_finished'] = str(time.time())
self.__additional_informations[event_name]['last_duration'] = str(time.time() - start_time)
return True
def unregister_event(self, event_name, event_source, delete_source_when_empty = True):
try:
logger.trace("unregister Event %s from %s ", event_name, event_source)
if event_name not in self.__Events: return "event unknown"
if event_source not in self.__Events[event_name]: return "source not know for this event"
self.__Events[event_name].remove(event_source)
if len(self.__Events[event_name]) is 0:
del self.__Events[event_name]
logger.debug("no more sources for event %s - remove event too", event_name)
if delete_source_when_empty: self.unregister_source(event_source)
logger.trace("event_source %s was removed for event %s", event_source, event_name)
return True
except Exception as exp:
logger.error('failed to unregister event %s with error message %s', event_name, exp)
return False
def unregister_source(self, event_source, force_unregister = False):
try:
logger.trace("unregister Eventsource %s and force_unregister is %s", event_source, force_unregister)
if event_source not in self.__Sources: return "event_source %s unknown" % (event_source)
for event_name in self.__Events.keys():
if event_source in self.__Events[event_name] and force_unregister:
self.unregister_event(event_name, event_source, False)
elif event_source in self.__Events[event_name] and not force_unregister:
return "couldn't unregister event_source %s because it is used for event %s" % (event_source, event_name)
if event_source in self.__Sources:
# sollte nicht nötig sein, da es entfernt wird, wenn das letzte Event dafür gelöscht wird
self.__Sources.remove(event_source)
logger.trace("event_source %s was removed", event_source)
return True
except Exception as exp:
logger.exception('failed to unregister source %s with error message %s', event_source, exp)
return False
def register_action(self, event_name, action_object, *args, **kwargs):
if ismethod(action_object) and callable(action_object):
action_object = SingleAction(action_object, *args, **kwargs)
elif isfunction(action_object) and callable(action_object):
action_object = SingleAction(action_object, *args, **kwargs)
elif not isinstance(action_object, SingleAction):
action_object = SingleAction.from_string(action_object)
if action_object is None:
logger.error('action_object is None')
return False
if 'single_fire_action' in kwargs.keys() and kwargs['single_fire_action'] is True:
action_object.single_fire_action = True
del kwargs['single_fire_action']
if event_name in self.__Actions:
self.__Actions[event_name].append(action_object)
logger.trace("action %s was added to event %s", action_object, event_name)
else:
self.__Actions[event_name] = [action_object]
logger.trace("action %s was added to new evententry %s", action_object, event_name)
return action_object
__call__ = fire_event_asynchron

View File

@ -0,0 +1,33 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
logger = logging.getLogger(__name__)
logger.debug("%s loaded", __name__)
def get(*args, **kwargs):
try:
if len(kwargs['name']) == 0: kwargs['name'] = ['']
if len(kwargs['value']) == 0: kwargs['value'] = ['']
#-- CODE by PAH
if kwargs['name'][0] == 'purge':
try:period = float(kwargs['value'][0])
except: period = 1.0
return kwargs['DoorPiObject'].event_handler.db.purge_logs(period)
else:
filter = kwargs['name'][0]
try: max_count = int(kwargs['value'][0])
except: max_count = 1000
return kwargs['DoorPiObject'].event_handler.db.get_event_log_entries(max_count, filter)
#-- END CODE by PAH
except Exception as exp:
logger.exception(exp)
return {'Error': 'could not create '+str(__name__)+' object - '+str(exp)}
def is_active(doorpi_object):
if len(doorpi_object.event_handler.db.get_event_log_entries(1, '')):
return True
else:
return False