Autor Thema: Nur zum Lernen mit der Bitte um konstruktive Kritik ...  (Gelesen 1420 mal)

Offline supernova1963

  • Sr. Member
  • ****
  • Beiträge: 518
Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« am: 29 August 2021, 11:21:19 »
Auf Empfehlung von @Beta-User verlagere ich das Thema hierhin (zeitlich von unten nach oben übernommen).

Offen bzw. von mir noch nicht wirklich verstanden sind die Anregungen von @Beta-User:
- zum split könnte man auch noch ein paar Takte sagen, spare ich mir im Moment;
- Welchen Vorteil bringt es while statt foreach zu verwenden?
- Warum soll der json Abschluss nicht innerhalb der Schleife erfolgen?

Letzter Stand (da waehleRaum($) und waehleZone($) nahezu identisch sind hier nur noch waehleRaum($) aus 99_myXiaomiUtils.pm):
##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;

use strict;
use warnings;

sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


########################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennte Räume in
# einen publish Befehl übersetzen. Übergeben wird in der setList $EVENT.
sub
waehleRaum($)

{
# Parameter $EVENT: $COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_);

# Prüfung, ob mind. ein Raumname als Parameter übergeben wurde
if (@evtparts < 2) {
Log "3", "Raum: ".$evtparts[0]." in waehleRaum() nicht definiert!";
return undef;
}
my $i = 0;
# Das erste Element in $EVENT ist der Befehl und wird entfernt
shift(@evtparts);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';

###Räume definieren##########################################################
#
my %rid = (Wohnzimmer=>1, Esszimmer=>2, Kueche=>3);
#
###ENDE Definitionen##########################################################

# Überprüfen der übergebenen Raumnamen als Leerzeichen getrennte Parameter  
foreach my $room (@evtparts) {

# Prüfung, ob Raumname im hash rid vorhanden
if (!defined($rid{$room})) {
Log "3", "Raum: ".$evtparts[$i]." in waehleRaum() nicht definiert!";
next;
}

# json - ($set_json) muss bei dem ersten Raum kein Komma voran gestellt werden
if ($i == 0) {
$set_json = $set_json.$rid{$room};
}

# json - ($set_json) nach ltz. Raum mit Klammern abgeschliessen
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$rid{$room}.'"}]}';
}

# json - ($set_json) ansderenfalls wird ein Komma voran gestellt
else {
$set_json =  $set_json.','.$rid{$room};
}

$i = $i + 1;
}

# Übergabe des auszuführenden publish Befehls
Log("5", "waehleRaum() hat ".$set_topic." ".$set_json." übergeben!");
return $set_topic." ".$set_json;

}

# Ende waehle Raum / Räume
########################################################################

1;

Danke,

Gernot

Verlauf bis hierhin:

@JensS: Bitte melden, wenn es zu OT ist (wie gesagt, ich fände das eigentlich in Perl für FHEM-User besser aufgehoben)...

Folgende Anmerkungen @supernova1963:
- "splice" ist eine nette Idee, hier aber eine "Spatzenkanone". Will man das erste Element aus einer Liste (ich finde "Liste" anschaulicher als "Array") löschen, ist "shift" m.E. das (schnellere und einfacherer) Mittel der Wahl - man muss das nicht zwingend dazu nutzen, einer Variable einen Wert zuzuweisen (den des gelöschten Elements); (vgl. auch "pop", und ergänzend noch push und unshift)
- zum split könnte man auch noch ein paar Takte sagen, spare ich mir im Moment;
- Es stellt sich die Frage, warum man überhaupt die Liste verändert, wenn man hinterher - im Prinzip - eine nummerische Schleife bauen will. Da kann man auch direkt "for (1..@evtparts-1) {" schreiben, dann man man die "Durchlaufnummer" in $_ stehen, und kann darüber ($evtparts[$_]) auf die Raumnamen in der Liste zugreifen.
- "zerstörend" auf die Liste zuzugreifen ist aber auch möglich und hier ggf. sogar eine gute Idee. Dann kann man nämlich die "Rest-Liste" für eine "while"-Schleife verwenden und das jeweils erste Element wieder innerhalb der Schleife per shift in eine Variable holen.

Sonstige Kritik:
- es gibt keine Prüfungen, ob
-- überhaupt ein Raum angegeben ist (return if @evtparts < 2;)
-- ein angegebener Raum gültig ist (next if !defined $rid{$room};)
Wegen letzterem Punkt würde ich den "finalen String" $set_json zunächst nicht belegen und erst nach der Schleife (siehe "qq") zusammenbauen, wenn der dann einen Wert hat.
(Schon klar, dass das kryptisch ist, viel Spaß beim knobeln ;) ).
Neuer Versuch 99_myXiaomiUtils.pm "besser" zu machen (immer noch rein theoretisch!):
##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;

use strict;
use warnings;

sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


########################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennten Räumen in
# einen publish Befehl übersetzen. Übergeben wird im Attribut des MQTT2_DEVICE setList { waehleRaum($EVENT) }.
sub
waehleRaum($)
{
#Parameter $EVENT: $EVTPART0='COMMAND' $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_);
# Das erste Element in $EVENT ist der Befehl und wird entfernt
@evtparts = splice(@evtparts, 0, 1);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';
my $i = 0;

# Räume definieren

my %rid = (Wohnzimmer=>1, Esszimmer=>2, Kueche=>3);

# ENDE Definitionen

# Erster und letzter Parameter werden separat zu $set_json hinzugefügt
foreach my $room (@evtparts) {
if ($i == 0) {
$set_json = $set_json.$rid{$room};
}
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$rid{$room}.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$room};
}
i = i + 1;
}

# Übergabe des auszuführenden publish Befehls
return $set_topic." ".$set_json;
}

# Ende waehle Raum / Räume
########################################################################

########################################################################
# Zonenname bzw. Namen von mehreren Leerzeichen getrennte Zonen in
# einen publish Befehl übersetzen. Übergeben wird im Attribut des MQTT2_DEVICE setList { waehleZone($EVENT) }.
sub
waehleZone($)
{
#Parameter $EVENT: COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_);
# Das erste Element in $EVENT ist der Befehl und wird entfernt
@evtparts = splice(@evtparts, 0, 1);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';
my $i = 0;
# Zonen definieren

my %zid = (Esstisch=>"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4");

# ENDE Definitionen

#   Überprüfen der übergebenen Zonennamen als Leerzeichen getrennte Parameter  
foreach my $zone (@evtparts) {
if ($i == 0) {
$set_json = $set_json.$zid{$zone};
}
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$zid{$zone}.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$zone};
}
i = i + 1;
}

# Übergabe des auszuführenden publish Befehls
return $set_topic." ".$set_json;

}

####################################

1;

@JensS: Ich bin nach-wie-vor gespannt auf die tatsächlich funktionierende Lösung
Sieht auf den ersten Blick ok aus :) .
Im Prinzip dürfte das Mindestziel erreicht sein, aber über Details zum Coding "könnte" man dann durchaus nochmal etwas intensiver nachdenken (wäre eigentlich ein guter Kandidat für einen peer review im (User-) Perl-Bereich ;) ).
Kurzfassung, was mir an Ansatzpunkten im Kopf wäre (muß nicht immer klappen):  "split" geschickter  wählen (\s+), elsif/else durch direktes return vermeiden und das "innere" Zusammenbauen in eine (1..@arr)-Schleife verpacken...
Nur zum "Mitlesen" und lernen ...

Ich bin gespannt auf die korrekte Umsetzung bzw. Lösung ...

Mein nicht getesteter laienhafter Vorschlag für das setList Attribut:
attr Sauger setList sauge_Raeume:noArg xiaomi {"action": {"siid": 4, "aiid": 13}}\
sauge_Zonen:noArg xiaomi {"action": {"siid": 6, "aiid": 5}}\
waehle_Zone:Esstisch { waehleZone($EVENT) }\
waehle_Raum:Wohnzimmer,Esszimmer,Küche { waehleRaum($EVENT) }\
waehle_Raeume { waehleRaum($EVENT) }\
waehle_Zonen { waehleZone($EVENT) }

Mein nicht getesteter laienhafter Vorschlag für 99_myXiaomiUtils.pm:
##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;

use strict;
use warnings;

sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


########################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennte Räume in
# einen publish Befehl übersetzen. Übergeben wird in der setList $EVENT.
sub
waehleRaum($)

{
my ($COMD,$ROOM1,$ROOM2,$ROOM3,$ROOM4,$ROOM5) = split(/ /,@_);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';

# Räume definieren

my %rid = (Wohnzimmer=>1, Esszimmer=>2, Kueche=>3);

# ENDE Definitionen

        #   Überprüfen der übergebenen Raumnamen als Leerzeichen getrennte Parameter
if ($ROOM1 eq "" || !defined($ROOM1) ) {
return undef;
}
else {
$set_json = $set_json.$rid{$ROOM1},
}

if ($ROOM2 eq "" || !defined($ROOM2) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$ROOM2};
}
if ($ROOM3 eq "" || !defined($ROOM3) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$ROOM3};
}
if ($ROOM4 eq "" || !defined($ROOM4) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$ROOM4};
}
if ($ROOM5 eq "" || !defined($ROOM5) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$ROOM5}.'"}]}';
}

# Übergabe des auszuführenden publish Befehls
return $set_topic." ".$set_json;

}

# Ende waehle Raum / Räume
########################################################################

########################################################################
# Zonenname bzw. Namen von mehreren Leerzeichen getrennte Zonen in
# einen publish Befehl übersetzen. Übergeben wird in der setList $EVENT.
sub
waehleZone($)
{
my ($COMD,$ZONE1,$ZONE2,$ZONE3,$ZONE4,$ZONE5) = split(/ /,@_);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';

# Zonen definieren

my %zid = (Esstisch=>"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4");

# ENDE Definitionen

# Überprüfen der übergebenen Zonennamen als Leerzeichen getrennte Parameter
if ($ZONE1 eq "" || !defined($ZONE1) ) {
return undef;
}
else {
$set_json = $set_json.$zid{$ZONE1},
}

if ($ZONE2 eq "" || !defined($ZONE2) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$ZONE2};
}
if ($ZONE3 eq "" || !defined($ZONE3) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$ZONE3};
}
if ($ZONE4 eq "" || !defined($ZONE4) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$ZONE4};
}
if ($ZONE5 eq "" || !defined($ZONE5) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$ZONE5}.'"}]}';
}

return $set_topic." ".$set_json;

}
# Ende waehle Zone / Zonen
########################################################################

1;
Hallo,

mein Saugroboter empfängt seine Anweisungen als JSON per MQTT.
waehle_Zone_Esstisch:noArg xiaomi {"set_properties": [{"siid": 6, "piid": 2, "value":"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4"}]}
sauge_gewaehlte_Zone:noArg xiaomi {"action": {"siid": 6, "aiid": 5}}
sauge_alles:noArg xiaomi {"action": {"siid": 2, "aiid": 1}}
waehle_Raueme:noArg xiaomi {"set_properties": [{"siid": 6, "piid": 2, "value":"1,2"}]}
sauge_Raeume::noArg xiaomi {"action": {"siid": 4, "aiid": 13}}
...
Nun würde ich gern vereinfachte Anweisungen "set Sauger sauge (Esstisch|alles|Raeume)" bzw. "set Sauger waehle (Zone|Raeume) (Esstisch|Wohnzimmer und Kueche)" nutzen.
Ist das im MQTT2DEVICE machbar oder muss ich einen anderen Weg suchen?

Gruß Jens

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 15739
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #1 am: 29 August 2021, 18:19:38 »
Vorab mal Danke, dass du die Anregung aufgegriffen hast. In deinem "Pseudocode" sind ein paar Dinge drin, die es m.E. wert sind, mal aufgegriffen zu werden, v.a., was Schleifen über Arrays angeht.

"split"
a) Das ist zum einen eine Perl-"built-in Funktion. Bei solchen (und nur bei solchen) kann/darf man die Klammern auch weglassen (im Unterschied zu "Pseudo-built-ins", die durch die Verwendung von Prototypes entstehen. Zu Prototypes will ich hier aber nichts weiter sagen, das hier nur am Rande und auch im Code nicht verändert; weitere Beispiele sind im Code unten entsprechend kommentiert).
b) Intern ist das erste Argument eine regex. Und da darf man zum einen dem Compiler gleich sagen, dass man einen match sucht, und zum anderen gibt es die (hier nicht relevante) Option, da "hinten" auch weitere Optionen mit zu verwenden. Weiter kann man es dem Compiler einfacher machen, wenn man klarer sagt, wo Anfang und Ende der regex sind.
Hier mal zwei Varianten:
my @evtparts = split m/\s+/, @_;my @evtparts = split m{\s+}, @_;
"while statt for"
a) foreach kann man direkt aus dem Perl-Wortschatz streichen, es ist funktional identisch mit "for"...
(Ergänzend: Wer weiß, was unless bedeutet, kann das auch direkt wieder vergessen...)
b) Du hattest die Idee, die Liste mit den Argumenten "zerstörend" zu modifizieren (durch das splice). Das "zerstörende" Rausnehmen von Elementen aus der Liste kann man auch nutzen, um die durchzugehen, ohne vorher groß die Länge zu ermitteln. Das macht den Code kompakter, da man weniger Variablen braucht. (Siehe Code unten). Wenn man mal verstanden hat, wie das funktioniert, ist es m.E. am Ende einfacher zu lesen...

"Warum den Zusammenbau außerhalb der Schleife"
Wenn man den Code insgesamt etwas anders aufbaut, kann man das Schleifenergebnis gleich dazu nutzen, um durch eine einzige Abfrage festzustellen, ob es überhaupt (sinnvolle) Angaben durch den User gab. Dazu wirft man erst mal eine nur initialisierte Variable (ohne Wert) in den Raum und schaut hinterher, ob die geändert wurde (also einen Wert hat). Da hier "zwischendurch" nicht viel passiert, wenn es gar keine Angabe gab, kann man das ohne großen Effizienzverlust auch "hinterher" machen.

Beispiel (auch von mir nicht getestet; es wird hoffentlich klarer, wenn man die auskommentierten Teile rauswirft):
sub
waehleRaum($)

{
#    Parameter $EVENT: $COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
    my @evtparts = split m{\s+/}, @_;
#    Das erste Element in $EVENT ist der Befehl und wird entfernt
    shift @evtparts; #Beta-User: ebenfalls Perl built-in

    my $set_topic = "xiaomi";
    my $set_json; #Beta-User: nur initialisieren = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';

###Räume definieren##########################################################
#   
    my %rid = (Wohnzimmer=>1, Esszimmer=>2, Kueche=>3);
#   
###ENDE Definitionen##########################################################
       
#    Überprüfen der übergebenen Raumnamen als Leerzeichen getrennte Parameter     
    while (@evtparts) {
        my $room = shift @evtparts;
#        Prüfung, ob Raumname im hash rid vorhanden
        if ( !defined $rid{$room} ) { #Beta-User: Perl-built-in
            Log("3", "Raum: $room in waehleRaum() nicht definiert!"); #Beta-User: KEIN Perl-built-in...!; einfache Quotes => Variable wird extrapoliert
            next;
        }
       
#        json - ($set_json) muss bei dem ersten Raum kein Komma voran gestellt werden
                $set_json .= $set_json ? ",$rid{$room}" : $rid{$room}; #Beta-User: kürzere Schreibweise, je nachdem, ob schon was da steht, wird ein Komma vorangestellt oder nicht
        #if ($i == 0) {
            #$set_json = $set_json.$rid{$room};
        #}

#        json - ($set_json) nach ltz. Raum mit Klammern abgeschliessen
        #elsif ($i == @evtparts -1) {
        #    $set_json =  $set_json.','.$rid{$room}.'"}]}';
        #}

#        json - ($set_json) ansderenfalls wird ein Komma voran gestellt
        #else {
        #    $set_json =  $set_json.','.$rid{$room};
        #}
       
        #$i = $i + 1;
    }

#    Übergabe des auszuführenden publish Befehls
        return if !$set_json; # hat die Variable nach der Schleife keinen Inhalt, gab es keine (gültige) Raumangabe...
        $set_json = qq{{"set_properties": [{"siid": 6, "piid": 2, "value":"
        $set_json"}]}}'; # Zusammenbau am Ende
    Log("5", "waehleRaum() hat ".$set_topic." ".$set_json." übergeben!");
    return "$set_topic $set_json"; #Beta-User: einfacher zu lesen...

}

Hoffe, das ist soweit einleuchtend?

(Da wir es hier mit MQTT2_DEVICE zu tun haben, könnte man ggf. sogar den Topic und/oder den Hash mit den Raumzuweisungen durch entsprechende Attribut-Angaben machen, aber das ist dann ggf. was für den anderen Thread und ich müßte erst mal in den Modul-Code schauen, welche Angaben in setList ersetzt werden ($DEVICETOPIC, $NAME, evtl. $JSONMAP).
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline supernova1963

  • Sr. Member
  • ****
  • Beiträge: 518
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #2 am: 30 August 2021, 01:14:24 »
Einfach nur genial, @Beta-User …

Jetzt habe auch ich es begriffen, welche Vorteile der Zusammenbau außerhalb der Schleife hat.
Auch die Hinweise zu split kann ich nachvollziehen, und, foreach und unless sind vergessen.

An die "perlspezifische Kurzschreibweise" von "if … then" kann ich mich jedoch ebenso wenig gewöhnen, wie an die Variablenauswertung in double quoted strings. Aber du hast natürlich recht, ich hätte korrekterweise auch single quotes verwenden müssen.

Vielen Dank für die lehrreichende Erklärungen,

Gernot

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 15739
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #3 am: 30 August 2021, 10:03:00 »
Danke für die Rückmeldung :) , freut mich, dass es dir weitergeholfen hat.

Bzgl. Quotes und "müssen": Viele Wege führen nach Rom, und es war ja nicht "falsch", die Concatenation "dreckig" zu schreiben.

Die "Kurzschreibweise" ist afaik gar nicht Perl-spezifisch - falls du nicht "elsif" meinst, sondern
$foo = $bla eq "string" ? "this" : "that";Diesen "ternären Operator" habe ich auch teils in meinen MySensors-Sketchen drin, C (bzw. C++) müßte das also auch kennen, siehe auch die Hinweise in https://www2.icp.uni-stuttgart.de/~hilfer/lehre/100-online/skriptum/html_book00/node34.html.

Wenn man sich mal dran gewöhnt hat, finde ich das - sauber notiert (!) - eigentlich in den meisten Fällen gut lesbar (auch in Attributen, da ist irgendwann eine if-else-Cascade mit "Vollcode" komplett unübersichtlich...)



Da wir grade bei C waren: Anlass war ja auch das mit den Schleifen gewesen. Vorhin habe ich hier im Forum das hier gefunden (Auszug, da sind irgendwelche Variablen vorher schon vorhanden, aber ich hoffe, es ist trotzdem erkennbar, dass es einfach nur zwei verschachtelte Schleifen sind):
  my $res = 0;
  for ($j = 0; $j < $len; $j++) {
    $val = $data[$j];
    for ($i = 0; $i < 8; $i++) {
      $tmp = ($res ^ $val) & 0x80;
      $res <<= 1;
      $res &= 0xFF;
      if ($tmp != 0) {
        $res ^= 0x31;
      }
      $val <<= 1;
    }
  }
  return $res;

Perlcritic bezeichnet sowas als "unschöne C-Style-loop" - wieder kein Verbrechen, aber eben "unschön" (level 2 (?), also "eigentlich vernachlässigbar).

Die "Perl-spezifische Schreibweise" wäre (ungetestet, und ich bin nicht ganz sicher, ob man $len oder $len-1 als höchsten Wert notieren muss (ich meine nein, weil die Erhöhung nach dem Schleifendurchlauf stattfindet); geht ja vor allem um's Prinzip):
  my $res = 0;
  for my $j (0..$len) { #Beta-User: Dieser Parameter $j ist nur in diesem lexical scope gültig und überschreibt auch einen außerhalb dieser Schleife existenten $j nicht!
    $val = $data[$j];
    for (0..7) {
      $tmp = ($res ^ $val) & 0x80;
      $res <<= 1;
      $res &= 0xFF;
      if ($tmp != 0) {
        $res ^= 0x31;
      }
      $val <<= 1;
    }
  }
  return $res;

Auch hier ist der Code im Ergebnis etwas kompakter und benötigt weniger Variablen, und man muss sich nicht um "$j" kümmern, was das "Umfeld" angeht, sondern kann "einfach so draufloscoden". $i ist hier ganz raus, man könnte (wie immer in Perl-Schleifen) dann auch $_ verwenden, wenn man den Wert doch braucht.
Nach meiner Erfahrung muss man aber grade beim Verschachteln von Schleifen (wie in obigem Beispiel) dann aufpassen, dass man sich nicht selbst durcheinanderbringt - ich würde daher im Sinne der Lesbarkeit dann in der inneren Schleife doch "for my $i ..." schreiben, wenn die grade aktuelle innere "Laufvariable" benötigt wird.

Ergo: unter Perlcritic-Gesichtspunkten zwar "ferner liefen", aber "schöner", wenn man es Perl-like macht :) .



Und als Ergänzung zu obigem "ternärem Operator" und der externen Fundstelle noch.

Da findet sich
int i1   =       3 > 4 ? 0 : 1;    //  i1   is 1
In meinen Codings findet sich "//" auch sehr häufig, hat dann aber eine komplett andere Bedeutung als in "C" - es ist kein Kommentar, sondern (vielleicht aus Sicht echter Perl-Experten etwas "dreckig" formuliert) eine sehr spezielle Form eines "ternären Operators": 

Perl "defined-or".

Um Abzusichern, dass unser Code-Beispiel wirklich nur ausgeführt wird, wenn auch ein Argument übergeben wird (in denem Code macht das die Prototype-Prüfung), würde ich z.B. schreiben:
sub waehleRaum {
    my $arg = shift // return;
    my $var = shift // q{Wert};
    [...]
bedeutet in Textform: führe ein return aus, wenn versucht wird, den Code ohne Argument auszuführen (oder mit echtem undef als erstem Argument), und weise der Variable $var den Wert "Wert" zu, falls der Code aufgerufen wird, ohne dass ein zweiter (valider) Parameter übergeben wird. (das "q" ist hier nur zur Ergänzung des Quote-Themas von oben, vollständige Fassung siehe https://www.perlmonks.org/?node_id=401006).
Zu defined-or siehe z.B. https://www.perl.com/article/32/2013/7/6/Use-the-logical-or-and-defined-or-operators-to-provide-default-subroutine-variable-behaviour/ (hab's nur überflogen).
« Letzte Änderung: 30 August 2021, 10:06:25 von Beta-User »
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline JensS

  • Sr. Member
  • ****
  • Beiträge: 910
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #4 am: 05 September 2021, 14:38:43 »
Bin erst heute zum testen gekommen. Mit ein paar kleinen Änderungen läuft's.
##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;

use strict;
use warnings;

sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


########################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennte Räume in
# einen publish Befehl übersetzen. Übergeben wird in der setList $EVENT.
sub
waehleRaum($)
{
# Parameter $EVENT: $COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_[0]);

# Prüfung, ob mind. ein Raumname als Parameter übergeben wurde
if (@evtparts < 2) {
Log "3", "Raum: ".$evtparts[0]." in waehleRaum() nicht definiert!";
return undef;
}
my $i = 0;
# Das erste Element in $EVENT ist der Befehl und wird entfernt
shift(@evtparts);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":';

###Räume definieren##########################################################
#
my %rid = (Wohnzimmer=> 1, Esszimmer=> 2, Kueche=> 3);
#
###ENDE Definitionen##########################################################

# Überprüfen der übergebenen Raumnamen als Leerzeichen getrennte Parameter
foreach my $room (@evtparts) {

# Prüfung, ob Raumname im hash rid vorhanden
if (!defined($rid{$room})) {
Log "3", "Raum: ".$evtparts[$i]." in waehleRaum() nicht definiert!";
next;
}

# json - ($set_json) muss bei dem ersten Raum kein Komma voran gestellt werden
if ($i == 0) {
$set_json = $set_json.$rid{$room}.'}]}';
}

# json - ($set_json) nach ltz. Raum mit Klammern abgeschliessen
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$rid{$room}.'}]}';
}

# json - ($set_json) ansderenfalls wird ein Komma voran gestellt
else {
$set_json =  $set_json.','.$rid{$room}.'}]}';
}

$i = $i + 1;
}

# Übergabe des auszuführenden publish Befehls
Log("5", "waehleRaum() hat ".$set_topic." ".$set_json." übergeben!");
return $set_topic." ".$set_json;

}

# Ende waehle Raum / Räume
########################################################################

########################################################################
# Zonenname bzw. Namen von mehreren Leerzeichen getrennte Zonen in
# einen publish Befehl übersetzen. Übergeben wird im Attribut des MQTT2_DEVICE setList { waehleZone($EVENT) }.
sub
waehleZone($)
{
# Parameter $EVENT: $COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_[0]);

# Prüfung, ob mind. ein Raumname als Parameter übergeben wurde
if (@evtparts < 2) {
Log "3", "Zone: ".$evtparts[0]." in waehleZone) nicht definiert!";
return undef;
}
my $i = 0;
# Das erste Element in $EVENT ist der Befehl und wird entfernt
shift(@evtparts);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":';

###Räume definieren##########################################################
#
my %zid = (Esstisch=>'"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4"', Eingang=>'"-0.58,-10,-0.58,-7.4,2.3,-7.4,2.3,-10"');
#
###ENDE Definitionen##########################################################

# Überprüfen der übergebenen Zonennamen als Leerzeichen getrennte Parameter
foreach my $zone (@evtparts) {

# Prüfung, ob Zonenname im hash rid vorhanden
if (!defined($zid{$zone})) {
Log "3", "Zone: ".$evtparts[$i]." in waehleZone() nicht definiert!";
next;
}

# json - ($set_json) muss bei der ersten Zone kein Komma voran gestellt werden
if ($i == 0) {
$set_json = $set_json.$zid{$zone}.'}]}';
}

# json - ($set_json) nach ltz. Zone mit Klammern abgeschliessen
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$zid{$zone}.'}]}';
}

# json - ($set_json) ansderenfalls wird ein Komma voran gestellt
else {
$set_json =  $set_json.','.$zid{$zone}.'}]}';
}

$i = $i + 1;
}

# Übergabe des auszuführenden publish Befehls
Log("5", "waehleZone() hat ".$set_topic." ".$set_json." übergeben!");
return $set_topic." ".$set_json;

}

####################################
1;

Vielen Dank für eure Hilfe! Damit lässt sich der Viomi deutlich besser steuern.

Gruß Jens
Debian auf APU2C4, HM-CFG-USB2, SIGNALduino, HM-ES-PMSw1-Pl, AB440S, AB440R, TFA 30.3121, TFA 30.3125, ITS-150, PIR-5000, configurable Firmata USB & LAN, 1-wire: DS-18B20, DS-18S20, DS-2408, DS-2413, diverse I2C-Komponenten, zigbee2mqtt, ESPEasy etc.

Offline supernova1963

  • Sr. Member
  • ****
  • Beiträge: 518
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #5 am: 06 September 2021, 09:53:22 »
Danke, für die Rückmeldung. Es freut mich sehr, dass es läuft.
In meinem Vorschlag war das zusammengesetzte json falsch. Zumindest bei den Raum Aufzählungen sollten kein Anführungszeichen vor und nach der Aufzählung der Räume notwendig sein!
Ebenso hätte es im split Befehl "@_[0]" sein müssen.   
Ich bin mir nicht ganz sicher, aber, wenn ich deinen Code richtig verstehe, dürfte der $set_json für mehrere Räume/Zonen, wie von Dir gepostet, nicht funktionieren, da dieser jetzt bei dem ersten, jedem weiteren und dem letzten angegebenen Raum mit .'}]}' quasi komplett abgeschlossen werden, oder wie sieht das korrekte json für mehrere Räume bzw. Zonen aus? 

Ich habe durch diese "Trockenübung" und der tollen Unterstützung von @Beta-User viel gelernt. Dafür noch einmal herzlichen Dank!
Zur Verwendung der verbesserten Variante von @Beta-User, und, der Erkenntnis, dass es für die Fehlersuche von Vorteil ist, sowohl das aufrufende fhem Device und den betreffenden setList command im Log anzugeben, hier an dieser Stelle mein aktueller Versuch.
Hinweis: Der Aufruf in setList erfolgt jetzt mit waehleRaum($NAME,$EVENT) bzw. waehleZone($NAME,$EVENT)!
Bitte prüfe das erzeugte json bei der Übergabe von 1 und mehreren Räumen/Zonen!

##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;
use strict;
use warnings;
our $VERSION = 0.0.1;


sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


#######################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennte Räume in
# einen Befehl im setList Attribut eines MQTT2_DEVICE übersetzen.
# Aufruf im Attribut setList: waehle_raum($NAME,$EVENT).
sub waehle_raum {
    my @arg = @_;
if (!@arg) {
Log('1','waehle_raum() wurde ohne Parameter aufgerufen: waehle_raum($NAME,$EVENT)!');
return;
}
elsif (@arg == 1) {
Log('1','waehle_raum() wurde mit nur einem Parameter aufgerufen: waehle_raum($NAME,$EVENT)!');
return;
}
my $name = shift @arg;
    my $event = shift @arg;
    my @evtparts = split m{\s+/}xms, $event;
    my $cmnd = shift @evtparts;
    my $set_topic;
    my $set_json;

### Räume definieren ###################################################
#
    my %rid = (Wohnzimmer=> 1, Esszimmer=> 2, Kueche=> 3);
#   
### ENDE Raum Definitionen #############################################

# Sammeln der übergebenen Raumnamen und übersetzen in json values
while (@evtparts) {
my $room = shift @evtparts;
# Prüfung, ob Raumname im hash rid vorhanden
if ( !defined $rid{$room} ) {
Log3($name, '3', 'Raum: '.$room.' in setList '.$cmnd.': waehle_raum() nicht definiert!');
next;
}
# json - ($set_json) bei den folgenden Räumen wird ein Komma voran gestellt
if ($set_json) {
$set_json = $set_json.q{,}.$rid{$room};
}
# json - ($set_json) muss bei dem ersten Raum kein Komma voran gestellt werden
else {
$set_json = $rid{$room};
}
}

# Prüfung, ob mind. ein Raumname als Parameter übergeben wurde
if (!$set_json) {
Log3($name, '3', 'Kein Raum in setList '.$cmnd.' waehle_raum() übergeben:'.$event );
return;
}
else {

### $set_json für Räume festlegen ########################################
#
$set_topic = 'xiaomi';
$set_json = '{{"set_properties": [{"siid": 6, "piid": 2, "value": '.$set_json.'}]}}';
#
### ENDE Raum json für Räume festlegen ###################################

Log3($name, '5', 'setList Befehl: '.$cmnd.' hat: '.$set_topic.q{ }.$set_json.' übergeben!');
}

# Übergabe des auszuführenden publish Befehls
return $set_topic.q{ }.$set_json;
}

##########################################################################
# Zonenname bzw. Name von mehreren Leerzeichen getrennten Zonene in
# einen Befehl im setList Attribut eines MQTT2_DEVICE übersetzen.
# Aufruf im Attribut setList: waehle_zone($NAME,$EVENT).
sub waehle_zone {
    my @arg = @_;
if (!@arg) {
Log('1','waehle_zone() wurde ohne Parameter aufgerufen: waehle_zone($NAME,$EVENT)!');
return;
}
elsif (@arg == 1) {
Log('1','waehle_zone() wurde mit nur einem Parameter aufgerufen: waehle_zone($NAME,$EVENT)!');
return;
}
    my $name = shift @arg;
    my $event = shift @arg;
    my @evtparts = split m{\s+/}xms, $event;
    my $cmnd = shift @evtparts;
    my $set_topic;
    my $set_json;

### Zonen definieren ####################################################
#
    my %zid = (Esstisch=>'"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4"', Eingang=>'"-0.58,-10,-0.58,-7.4,2.3,-7.4,2.3,-10"');
#   
### ENDE Zonen Definitionen #############################################

# Sammeln der übergebenen Zonennamen und übersetzen in json values
while (@evtparts) {
my $zone = shift @evtparts;
# Prüfung, ob Zonennamen im hash zid vorhanden
if ( !defined $zid{$zone} ) {
Log3($name, '3', 'Zone: '.$zone.' in setList '.$cmnd.': waehle_zone() nicht definiert!');
next;
}
# json - ($set_json) bei den folgenden Zonene wird ein Komma und Anführungszeichen voran gestellt
if ($set_json) {
$set_json = $set_json.q{,}.$zid{$zone};
}
# json - ($set_json) muss bei dem ersten Zonen kein Komma und Anführungszeichen voran gestellt werden
else {
$set_json = $set_json.$zid{$zone};
}
}

# Prüfung, ob mind. ein Zonenname als Parameter übergeben wurde
if (!$set_json) {
Log3($name, '3', 'Keine Zone in setList '.$cmnd.' waehle_zone() übergeben:'.$event );
return;
}
else {

### $set_json für Zonen festlegen ########################################
#
$set_topic = 'xiaomi';
$set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":'.$set_json.'}]}}';
#
### ENDE set_json für Zonen festlegen ###################################

Log3($name, '5', 'setList Befehl: '.$cmnd.' hat: '.$set_topic.q{ }.$set_json.' übergeben!');
}

# Übergabe des auszuführenden publish Befehls
return $set_topic.q{ }.$set_json;
}

##########################################################################

1;

Edit: Vergleichsfehler auf Hinweis von @Beta-User korrigiert
« Letzte Änderung: 06 September 2021, 13:32:15 von supernova1963 »

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 15739
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #6 am: 06 September 2021, 11:05:58 »
Danke, für die Rückmeldung. Es freut mich sehr, dass es läuft.
Schließe mich an, auch wenn mich das konkrete Ergebnis etwas "irritiert" hat. Aber TMTOWTDI...

Zitat
Ich habe durch diese "Trockenübung" und der tollen Unterstützung von @Beta-User viel gelernt. Dafür noch einmal herzlichen Dank!
:) Gerne und Danke für's Mitmachen!

Noch ein paar Hinweise zum letzten Vorschlag:

   if (@arg = 0) {
Da paßt die Syntax nicht, wenn, dann braucht man keine (nicht funktionierende?) Zuweisung, sondern einen nummerischen Vergleich (==)

Aber warum nicht gleich so:
   if (!@arg) {

Fast dasselbe gilt für
   elsif (@arg = 1) {
allerdings: Wenn vorher der Zweig mit einem "return;" endet, braucht man auch an der Stelle kein "else"  ;) .

Ich würde das mit "defined-or" lösen:

   my $name = shift @arg // return Log('1','waehle_raum() wurde ohne Parameter aufgerufen: waehle_raum($NAME,$EVENT)!');
    my $event = shift @arg // return Log('1','waehle_raum() wurde mit nur einem Parameter aufgerufen: waehle_raum($NAME,$EVENT)!');
   
Hinweis:
Log liefert afaik nichts zurück, sonst ggf, auch:
   my $name = shift @arg // Log('1','waehle_raum() wurde ohne Parameter aufgerufen: waehle_raum($NAME,$EVENT)!') && return;
    my $event = shift @arg // Log('1','waehle_raum() wurde mit nur einem Parameter aufgerufen: waehle_raum($NAME,$EVENT)!') && return;
   

Und die Concatenation mit ".=" gefällt wohl auch nicht, ebensowenig wie die Extrapolation in doppelten Quotes...?
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline supernova1963

  • Sr. Member
  • ****
  • Beiträge: 518
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #7 am: 06 September 2021, 13:26:24 »

   if (@arg = 0) {
Da paßt die Syntax nicht, wenn, dann braucht man keine (nicht funktionierende?) Zuweisung, sondern einen nummerischen Vergleich (==)

Habe ich korrigiert, s.o.

Alles weitere sieht hoch professionell aus und ist sicher viel kürzer, würde aber dazu führen, dass ich es selbst nach einiger Zeit nicht mehr verstehe (perl::Critic hat auch nicht gemeckert …)


Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 15739
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #8 am: 06 September 2021, 13:49:41 »
Alles weitere sieht hoch professionell aus und ist sicher viel kürzer, würde aber dazu führen, dass ich es selbst nach einiger Zeit nicht mehr verstehe
:) Auch ein wichtiger Grundsatz: So coden, dass man selbst es (voraussichtlich) dauerhaft nachvollziehen kann, was das eigentlich soll...
(Das ist der eigentliche Grund, warum ich kein "unless" mehr neu irgendwo hinschreibe...)

Perl-"defined-or" findet sich in meinem Code allerdings so häufig, dass ich gar keine Chance habe, das wieder zu vergessen ;D ...

Und wenn man's mal "intus" hat, ist es super-easy, genau wie "||" ("//" ist ja dasselbe nur "schräggestellt"!) und "&&", nur das man die beiden eben in der Regel schon als "alte Bekannte" begrüßt.
Bei der Gelegenheit: "and", "or"  und "not" bitte auch (für die meisten Fälle) direkt aus dem Perl-Wortschatz streichen! (Wer sich fragt, wieso: "Mixed high and low-precedence booleans", z.B. in https://www.perlmonks.org/?node_id=1167582).
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline frober

  • Moderator
  • Sr. Member
  • ***
  • Beiträge: 845
  • Was man nicht kann, kann man lernen...
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #9 am: 06 September 2021, 18:36:22 »
:) Auch ein wichtiger Grundsatz: So coden, dass man selbst es (voraussichtlich) dauerhaft nachvollziehen kann, was das eigentlich soll...
(Das ist der eigentliche Grund, warum ich kein "unless" mehr neu irgendwo hinschreibe...)

Perl-"defined-or" findet sich in meinem Code allerdings so häufig, dass ich gar keine Chance habe, das wieder zu vergessen ;D ...

Und wenn man's mal "intus" hat, ist es super-easy, genau wie "||" ("//" ist ja dasselbe nur "schräggestellt"!) und "&&", nur das man die beiden eben in der Regel schon als "alte Bekannte" begrüßt.
Bei der Gelegenheit: "and", "or"  und "not" bitte auch (für die meisten Fälle) direkt aus dem Perl-Wortschatz streichen! (Wer sich fragt, wieso: "Mixed high and low-precedence booleans", z.B. in https://www.perlmonks.org/?node_id=1167582).

Sehe ich auch so und die Verneinung mit ! kann man immer Mal gebrauchen.
Z.B.
if ($a==!$b) {}oder
$a==!$aWenn wann z.B. on/off tauschen möchte...z.B. beim auto. Schaltvorgang.
Raspi 3b mit Raspbian Stretch und relativ aktuellem Fhem,  FS20, LGW, PCA301, MySensors mit RS485(CAN-Receiver), etc.,
einiges umgesetzt, vieles in Planung :-)

********************************************
...man wächst mit der Herausforderung...

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 15739
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #10 am: 06 September 2021, 19:11:16 »
 :o ähm, kann man evtl. in Teilen so notieren, aber es kommt mir komisch vor...
Eher:
if ( $a != $b ) {}

Und gemeint ist wohl der Tausch von Wahrheitswerten?
$a = !$a(glaube aber nicht, dass "off" "unwahr" in diesem Sinne ist, das müßte man vorher in was nummerisches konvertiert haben, oder...?)
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline frober

  • Moderator
  • Sr. Member
  • ***
  • Beiträge: 845
  • Was man nicht kann, kann man lernen...
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #11 am: 06 September 2021, 19:15:19 »
:o ähm, kann man evtl. in Teilen so notieren, aber es kommt mir komisch vor...
Eher:
if ( $a != $b ) {}

Und gemeint ist wohl der Tausch von Wahrheitswerten?
$a = !$a(glaube aber nicht, dass "off" "unwahr" in diesem Sinne ist, das müßte man vorher in was nummerisches konvertiert haben, oder...?)

Oh, da habe ich, glaube ich, etwas mit C/C++ (Arduino)/ oder überhaupt durcheinander gebracht. :o sorry
Raspi 3b mit Raspbian Stretch und relativ aktuellem Fhem,  FS20, LGW, PCA301, MySensors mit RS485(CAN-Receiver), etc.,
einiges umgesetzt, vieles in Planung :-)

********************************************
...man wächst mit der Herausforderung...

Offline JensS

  • Sr. Member
  • ****
  • Beiträge: 910
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #12 am: 07 September 2021, 21:15:26 »
Mit mehreren Räumen hatte ich es tatsächlich nicht probiert. Mehrere Zonen scheint der Viomi nicht zu unterstützen.
In der Datei musste ich nur eine } zur Erstellung des json pro sub entfernen. Seltsam ist, dass my @evtparts = split m{\s+/}xms, $event; nicht splittet. Es wird nur ein Element mit dem Inhalt des kompletten $EVENT erzeugt.my @evtparts = split /\ /, $event;dagegen funktioniert.  :o
##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;
use strict;
use warnings;
our $VERSION = 0.0.1;


sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


#######################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennte Räume in
# einen Befehl im setList Attribut eines MQTT2_DEVICE übersetzen.
# Aufruf im Attribut setList: waehle_raum($NAME,$EVENT).
sub waehle_raum {
    my @arg = @_;
if (!@arg) {
Log('1','waehle_raum() wurde ohne Parameter aufgerufen: waehle_raum($NAME,$EVENT)!');
return;
}
elsif (@arg == 1) {
Log('1','waehle_raum() wurde mit nur einem Parameter aufgerufen: waehle_raum($NAME,$EVENT)!');
return;
}
my $name = shift @arg;
    my $event = shift @arg;
    my @evtparts = split /\ /, $event;
    my $cmnd = shift @evtparts;
    my $set_topic;
    my $set_json;

### Räume definieren ###################################################
#
    my %rid = (Wohnzimmer=> 1, Esszimmer=> 2, Kueche=> 3);
#   
### ENDE Raum Definitionen #############################################

# Sammeln der übergebenen Raumnamen und übersetzen in json values
while (@evtparts) {
my $room = shift @evtparts;
# Prüfung, ob Raumname im hash rid vorhanden
if ( !defined $rid{$room} ) {
Log3($name, '3', 'Raum: '.$room.' in setList '.$cmnd.': waehle_raum() nicht definiert!');
next;
}
# json - ($set_json) bei den folgenden Räumen wird ein Komma voran gestellt
if ($set_json) {
$set_json = $set_json.q{,}.$rid{$room};
}
# json - ($set_json) muss bei dem ersten Raum kein Komma voran gestellt werden
else {
$set_json = $rid{$room};
}
}

# Prüfung, ob mind. ein Raumname als Parameter übergeben wurde
if (!$set_json) {
Log3($name, '3', 'Kein Raum in setList '.$cmnd.' waehle_raum() übergeben:'.$event );
return;
}
else {

### $set_json für Räume festlegen ########################################
#
$set_topic = 'xiaomi';
$set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value": "'.$set_json.'"}]}';
#
### ENDE Raum json für Räume festlegen ###################################

Log3($name, '5', 'setList Befehl: '.$cmnd.' hat: '.$set_topic.q{ }.$set_json.' übergeben!');
}

# Übergabe des auszuführenden publish Befehls
return $set_topic.q{ }.$set_json;
}

##########################################################################
# Zonenname bzw. Name von mehreren Leerzeichen getrennten Zonene in
# einen Befehl im setList Attribut eines MQTT2_DEVICE übersetzen.
# Aufruf im Attribut setList: waehle_zone($NAME,$EVENT).
sub waehle_zone {
    my @arg = @_;
if (!@arg) {
Log('1','waehle_zone() wurde ohne Parameter aufgerufen: waehle_zone($NAME,$EVENT)!');
return;
}
elsif (@arg == 1) {
Log('1','waehle_zone() wurde mit nur einem Parameter aufgerufen: waehle_zone($NAME,$EVENT)!');
return;
}
    my $name = shift @arg;
    my $event = shift @arg;
    my @evtparts = split /\ /, $event;
fhem("setreading Viomi test $evtparts[1]");
    my $cmnd = shift @evtparts;
    my $set_topic;
    my $set_json;

### Zonen definieren ####################################################
#
    my %zid = (Esstisch=>'"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4"', Eingang=>'"-0.58,-10,-0.58,-7.4,2.3,-7.4,2.3,-10"');
#   
### ENDE Zonen Definitionen #############################################

# Sammeln der übergebenen Zonennamen und übersetzen in json values
while (@evtparts) {
my $zone = shift @evtparts;
# Prüfung, ob Zonennamen im hash zid vorhanden
if ( !defined $zid{$zone} ) {
Log3($name, '3', 'Zone: '.$zone.' in setList '.$cmnd.': waehle_zone() nicht definiert!');
next;
}
# json - ($set_json) bei den folgenden Zonene wird ein Komma und Anführungszeichen voran gestellt
if ($set_json) {
$set_json = $set_json.q{,}.$zid{$zone};
}
# json - ($set_json) muss bei dem ersten Zonen kein Komma und Anführungszeichen voran gestellt werden
else {
$set_json = $set_json.$zid{$zone};
}
}

# Prüfung, ob mind. ein Zonenname als Parameter übergeben wurde
if (!$set_json) {
Log3($name, '3', 'Keine Zone in setList '.$cmnd.' waehle_zone() übergeben:'.$event );
return;
}
else {

### $set_json für Zonen festlegen ########################################
#
$set_topic = 'xiaomi';
$set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":'.$set_json.'}]}';
#
### ENDE set_json für Zonen festlegen ###################################

Log3($name, '5', 'setList Befehl: '.$cmnd.' hat: '.$set_topic.q{ }.$set_json.' übergeben!');
}

# Übergabe des auszuführenden publish Befehls
return $set_topic.q{ }.$set_json;
}

##########################################################################

1;

Gruß und Dank für die umfangreiche Hilfe!
Debian auf APU2C4, HM-CFG-USB2, SIGNALduino, HM-ES-PMSw1-Pl, AB440S, AB440R, TFA 30.3121, TFA 30.3125, ITS-150, PIR-5000, configurable Firmata USB & LAN, 1-wire: DS-18B20, DS-18S20, DS-2408, DS-2413, diverse I2C-Komponenten, zigbee2mqtt, ESPEasy etc.

Offline supernova1963

  • Sr. Member
  • ****
  • Beiträge: 518
Antw:Nur zum Lernen mit der Bitte um konstruktive Kritik ...
« Antwort #13 am: 08 September 2021, 06:09:15 »
Hallo @JensS,

den split Befehl und die doppelte "{" habe ich irgendwie versaubeutelt.
@Beta-User hat split m{\s+}, @_;
# oder
split m/\s+/, @_;
empfohlen, das als Trennzeichen space, tab oder newline zwischen den Räumen/Zonen erlaubt.

Sorry, und noch einmal Danke für die Rückmeldung,

Gernot
Zustimmung Zustimmung x 1 Liste anzeigen