Merge pull request #6 from CoolTuxNet/patch-cooltux

Anmerkungen und Codeanalyse
This commit is contained in:
Manfred 2022-11-23 21:14:03 +01:00 committed by GitHub
commit 23d037f2a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 218 additions and 75 deletions

View File

@ -25,7 +25,7 @@
##########################################################################
# $Id: 98_Matrix.pm 14063 2022-11-12 12:52:00Z Man-fred $
package FHEM::Devices::Matrix;
package FHEM::Matrix;
use strict;
use warnings;
use HttpUtils;
@ -33,8 +33,8 @@ use FHEM::Meta;
use GPUtils qw(GP_Export GP_Import);
use JSON;
use vars qw(%data);
use FHEM::Core::Authentication::Passwords qw(:ALL);
use vars qw(%data); #(CoolTux) sollte auch nicht nötig sein da Du es in dem Package nichts verwendest
# use FHEM::Core::Authentication::Passwords qw(:ALL); #(CoolTux) Kann raus da Du es ja hier nicht verwendest
require FHEM::Devices::Matrix::Matrix;
#-- Run before package compilation

View File

@ -18,6 +18,8 @@ use JSON;
#use vars qw(%data);
use FHEM::Core::Authentication::Passwords qw(:ALL);
use experimental qw /switch/; #(CoolTux) - als Ersatz für endlos lange elsif Abfragen
# strftime
# RemoveInternalTimer
# readingFnAttributes
@ -54,7 +56,10 @@ sub Attr_List{
}
sub Define {
my ($hash, $def) = @_;
#(CoolTux) bei einfachen übergaben nimmt man die Daten mit shift auf
my $hash = shift;
my $def = shift;
my @param = split('[ \t]+', $def);
my $name = $param[0]; #$param[0];
@ -63,6 +68,7 @@ sub Define {
if(int(@param) < 1) {
return "too few parameters: define <name> Matrix <server> <user>";
}
$hash->{name} = $param[0];
$hash->{server} = $param[2];
$hash->{user} = $param[3];
@ -70,52 +76,87 @@ sub Define {
$hash->{helper}->{passwdobj} = FHEM::Core::Authentication::Passwords->new($hash->{TYPE});
#$hash->{helper}->{i18} = Get_I18n();
$hash->{NOTIFYDEV} = "global";
Startproc($hash) if($init_done);
Startproc($hash) if($init_done); #(CoolTux) Wie startet Startproc() wenn $init_done 0 ist. Dann bleibt das Modul stehen und macht nichts mehr
# es empfiehlt sich hier in der NotifyFn das globale Event INITIALIZED abzufangen.
# Ok gerade gesehen hast Du gemacht!!!
return ;
}
sub Undef {
my ($hash, $arg) = @_;
my $hash = shift;
my $arg = shift;
my $name = $hash->{NAME};
# undef $data
$data{MATRIX}{"$name"} = undef;
$hash->{helper}->{passwdobj}->setDeletePassword($name);
$data{MATRIX}{"$name"} = undef; #(CoolTux) Bin mir gerade nicht sicher woher das $data kommt
# meinst Du das %data aus main? Das ist für User. Wenn Du als Modulentwickler
# etwas zwischenspeichern möchtest dann in $hash->{helper}
$hash->{helper}->{passwdobj}->setDeletePassword($name); #(CoolTux) das ist nicht nötig,
# du löschst jedesmal den Eintrag wenn FHEM beendet wird.
# Es sollte eine DeleteFn geben da kannst Du das rein machen
return ;
}
sub Startproc {
my ($hash) = @_;
my $hash = shift;
my $name = $hash->{NAME};
Log3($name, 4, "$name : Matrix::Startproc $hash ".AttrVal($name,'matrixPoll','-1'));
# Update necessary?
Log3($name, 1, "$name: Start V".$hash->{ModuleVersion}." -> V".$Module_Version) if ($hash->{ModuleVersion});
$hash->{ModuleVersion} = $Module_Version;
$language = AttrVal('global','language','EN');
$data{MATRIX}{"$name"}{"softfail"} = 1;
Login($hash) if (AttrVal($name,'matrixPoll',0) == 1);
return;
}
sub Login {
my ($hash) = @_;
my $hash = shift;
Log3($hash->{NAME}, 4, "$hash->{NAME} : Matrix::Login $hash");
return PerformHttpRequest($hash, 'login', '');
}
##########################
sub Notify($$)
# sub Notify($$)
#(CoolTux) Subroutine prototypes used. See page 194 of PBP (Subroutines::ProhibitSubroutinePrototypes)
# Contrary to common belief, subroutine prototypes do not enable
# compile-time checks for proper arguments. Don't use them.
sub Notify
{
my ($hash, $dev) = @_;
my $hash = shift;
my $dev = shift;
my $name = $hash->{NAME};
my $devName = $dev->{NAME};
return "" if(IsDisabled($name));
my $events = deviceEvents($dev,1);
return if( !$events );
if(($devName eq "global") && grep(m/^INITIALIZED|REREADCFG$/, @{$events}))
#if(($devName eq "global") && grep(m/^INITIALIZED|REREADCFG$/, @{$events}))
#(CoolTux) unnötige Klammern, und vielleicht bisschen übersichtlicher versuchen :-)
if ( $devName eq "global"
&& grep(m/^INITIALIZED|REREADCFG$/, @{$events}))
{
Log3($name, 4, "$name : Matrix::Notify $hash");
Startproc($hash);
}
#(CoolTux) bin mir nicht sicher wieso die Schleife. Nötig ist sie aber egal wofür gedacht nicht.
foreach my $event (@{$events}) {
$event = "" if(!defined($event));
### Writing log entry
@ -129,25 +170,41 @@ sub Notify($$)
#
# processing $event with further code
}
return undef;
return; #(CoolTux) es reicht nur return. Wichtig jede sub muss immer mit return enden
}
#############################################################################################
# called when the device gets renamed, copy from telegramBot
# in this case we then also need to rename the key in the token store and ensure it is recoded with new name
sub Rename($$) {
my ($new,$old) = @_;
sub Rename {
my $new = shift;
my $old = shift;
my $hash = $defs{$new};
$data{MATRIX}{"$new"} = $data{MATRIX}{"$old"};
$data{MATRIX}{"$old"} = undef;
$hash->{helper}->{passwdobj}->setRename($new,$old);
#$data{MATRIX}{"$old"} = undef; (CoolTux) Wenn ein Hash nicht mehr benötigt wird dann delete
delete $data{MATRIX}{"$old"}
my ($passResp,$passErr);
($passResp,$passErr) = $hash->{helper}->{passwdobj}->setRename($new,$old); #(CoolTux) Es empfiehlt sich ab zu fragen ob der Wechsel geklappt hat
Log3($name, 1, "$name : Matrix::Rename - error while change the password hash after rename - $passErr")
if ( !defined($passResp)
&& defined($passErr) );
Log3($name, 1, "$name : Matrix::Rename - change password hash after rename successfully")
if ( defined($passResp)
&& !defined($passErr) );
#my $nhash = $defs{$new};
return;
}
sub I18N {
my $value = shift;
my $def = {
'EN' => {
'require2' => 'requires 2 arguments'
@ -166,22 +223,32 @@ sub Get {
my $value = join(" ", @args);
#$cmd = '?' if (!$cmd);
if ($cmd eq "wellknown") {
#(CoolTux) Eine endlos Lange elsif Schlange ist nicht zu empfehlen, besser mit switch arbeiten
# Im Modulkopf use experimental qw /switch/; verwenden
given ($cmd) {
when ('wellknown') {
return PerformHttpRequest($hash, $cmd, '');
}
elsif ($cmd eq "logintypes") {
when ('logintypes') {
return PerformHttpRequest($hash, $cmd, '');
}
elsif ($cmd eq "sync") {
$data{MATRIX}{"$name"}{"softfail"} = 0;
when ('sync') {
$data{MATRIX}{"$name"}{"softfail"} = 0; #(CoolTux) Bin mir gerade nicht sicher woher das $data kommt
# meinst Du das %data aus main? Das ist für User. Wenn Du als Modulentwickler
# etwas zwischenspeichern möchtest dann in $hash->{helper}
$data{MATRIX}{"$name"}{"hardfail"} = 0;
return PerformHttpRequest($hash, $cmd, '');
}
elsif ($cmd eq "filter") {
when ('filter') {
return qq("get Matrix $cmd" needs a filterId to request);
return PerformHttpRequest($hash, $cmd, $value);
}
return "Unknown argument $cmd, choose one of logintypes filter sync wellknown";
default { return "Unknown argument $cmd, choose one of logintypes filter sync wellknown"; }
}
}
sub Set {
@ -192,49 +259,89 @@ sub Set {
#Log3($name, 5, "Set $hash->{NAME}: $name - $cmd - $value");
#return "set $name needs at least one argument" if (int(@$param) < 3);
if ($cmd eq "msg") {
#(CoolTux) Eine endlos Lange elsif Schlange ist nicht zu empfehlen, besser mit switch arbeiten
# Im Modulkopf use experimental qw /switch/; verwenden
# if ($cmd eq "msg") {
# return PerformHttpRequest($hash, $cmd, $value);
# }
# elsif ($cmd eq "pollFullstate") {
# readingsSingleUpdate($hash, $cmd, $value, 1); # Readings erzeugen
# }
# elsif ($cmd eq "password") {
# my ($erg,$err) = $hash->{helper}->{passwdobj}->setStorePassword($name,$value);
# return undef;
# }
# elsif ($cmd eq "filter") {
# return PerformHttpRequest($hash, $cmd, '');
# }
# elsif ($cmd eq "question") {
# return PerformHttpRequest($hash, $cmd, $value);
# }
# elsif ($cmd eq "questionEnd") {
# return PerformHttpRequest($hash, $cmd, $value);
# }
# elsif ($cmd eq "register") {
# return PerformHttpRequest($hash, $cmd, ''); # 2 steps (ToDo: 3 steps empty -> dummy -> registration_token o.a.)
# }
# elsif ($cmd eq "login") {
# return PerformHttpRequest($hash, $cmd, '');
# }
# elsif ($cmd eq "refresh") {
# return PerformHttpRequest($hash, $cmd, '');
# }
# else {
# return "Unknown argument $cmd, choose one of filter:noArg password question questionEnd pollFullstate:0,1 msg register login:noArg refresh:noArg";
# }
given ($cmd) {
when ('msg') {
return PerformHttpRequest($hash, $cmd, $value);
}
elsif ($cmd eq "pollFullstate") {
when ('pollFullstate') {
readingsSingleUpdate($hash, $cmd, $value, 1); # Readings erzeugen
}
elsif ($cmd eq "password") {
when ('password') {
my ($erg,$err) = $hash->{helper}->{passwdobj}->setStorePassword($name,$value);
return undef;
return;
}
elsif ($cmd eq "filter") {
when ('filter') {
return PerformHttpRequest($hash, $cmd, '');
}
elsif ($cmd eq "question") {
when ('question') {
return PerformHttpRequest($hash, $cmd, $value);
}
elsif ($cmd eq "questionEnd") {
when ('questionEnd') {
return PerformHttpRequest($hash, $cmd, $value);
}
elsif ($cmd eq "register") {
when ('register') {
return PerformHttpRequest($hash, $cmd, ''); # 2 steps (ToDo: 3 steps empty -> dummy -> registration_token o.a.)
}
elsif ($cmd eq "login") {
when ('login') {
return PerformHttpRequest($hash, $cmd, '');
}
elsif ($cmd eq "refresh") {
when ('refresh') {
return PerformHttpRequest($hash, $cmd, '');
}
else {
return "Unknown argument $cmd, choose one of filter:noArg password question questionEnd pollFullstate:0,1 msg register login:noArg refresh:noArg";
}
#return "$opt set to $value. Try to get it.";
default {
return "Unknown argument $cmd, choose one of filter:noArg password question questionEnd pollFullstate:0,1 msg register login:noArg refresh:noArg";
}
}
return;
}
sub Attr {
my ($cmd,$name,$attr_name,$attr_value) = @_;
Log3($name, 4, "Attr - $cmd - $name - $attr_name - $attr_value");
if($cmd eq "set") {
if ($attr_name eq "matrixQuestion_") {
my @erg = split(/ /, $attr_value, 2);
#$_[2] = "matrixQuestion_n";
$_[2] = "matrixQuestion_n";
return qq("attr $name $attr_name" ).I18N('require2') if (!$erg[1] || $erg[0] !~ /[0-9]/);
$_[2] = "matrixQuestion_$erg[0]";
$_[3] = $erg[1];
@ -246,11 +353,15 @@ sub Attr {
$_[3] = $erg[1];
}
}
return ;
}
sub Get_Message($$$) {
my($name, $def, $message) = @_;
sub Get_Message {
my $name = shift;
my $def = shift;
my $message = shift;
Log3($name, 3, "$name - $def - $message");
my $q = AttrVal($name, "matrixQuestion_$def", "");
my $a = AttrVal($name, "matrixAnswer_$def", "");
@ -259,8 +370,16 @@ sub Get_Message($$$) {
my @answers = split(':', $a);
Log3($name, 3, "$name - $q - $a");
my $pos = 0;
my ($question, $answer);
foreach $question (@questions){
#my ($question, $answer);
my $answer;
# foreach my $question (@questions){
foreach my $question (@questions){ #(CoolTux) - Loop iterator is not lexical. See page 108 of PBP (Variables::RequireLexicalLoopIterators)perlcritic
# This policy asks you to use `my'-style lexical loop iterator variables:
# foreach my $zed (...) {
# ...
# }
Log3($name, 3, "$name - $question - $answers[$pos]");
$answer = $answers[$pos] if ($message eq $question);
if ($answer){
@ -270,11 +389,18 @@ sub Get_Message($$$) {
}
$pos++;
}
return;
}
sub PerformHttpRequest($$$)
sub PerformHttpRequest
{
my ($hash, $def, $value) = @_;
#(CoolTux) hier solltest Du überlegen das Du die einzelnen Anweisung nach der Bedingung in einzelne Funktionen auslagerst
# Subroutine "PerformHttpRequest" with high complexity score
my $hash = shift;
my $def = shift;
my $value = shift;
my $now = gettimeofday();
my $name = $hash->{NAME};
my $passwd = "";
@ -424,12 +550,21 @@ sub PerformHttpRequest($$$)
Log3($name, 3, qq($name $param->{'msgnumber'} $def Request Busy/Sync $data{MATRIX}{"$name"}{"busy"} / $data{MATRIX}{"$name"}{"sync"}) );
HttpUtils_NonblockingGet($param); # Starten der HTTP Abfrage. Es gibt keinen Return-Code.
return undef;
return;
}
sub ParseHttpResponse($)
sub ParseHttpResponse
{
my ($param, $err, $data) = @_;
#(CoolTux) hier solltest Du überlegen das Du die einzelnen Anweisung nach der Bedingung in einzelne Funktionen auslagerst
# Subroutine "PerformHttpRequest" with high complexity score
my $param = shift;
my $err = shift;
my $data = shift;
my $hash = $param->{hash};
my $def = $param->{def};
my $value = $param->{value};
@ -593,6 +728,7 @@ sub ParseHttpResponse($)
#m.relates_to
}
}
readingsEndUpdate($hash, 1);
$data{MATRIX}{"$name"}{"busy"}--; # = $data{MATRIX}{"$name"}{"busy"} - 1; # queue is busy until response is received
$data{MATRIX}{"$name"}{"sync"}-- if ($def eq "sync"); # possible next sync
@ -632,4 +768,11 @@ sub ParseHttpResponse($)
}
}
# Damit ist die Abfrage zuende.
return;
}
1; #(CoolTux) ein Modul endet immer mit 1;
__END__ #(CoolTux) Markiert im File das Ende des Programms. Danach darf beliebiger Text stehen. Dieser wird vom Perlinterpreter nicht berücksichtigt.