[gelöst] Wie kann ich mehrere "MQTT publish" Befehle einem set Befehl zuordnen?

Begonnen von supernova1963, 24 März 2020, 18:34:05

Vorheriges Thema - Nächstes Thema

supernova1963

Hallo zusammen,

ich habe 2 warm- und kaltweiss LED-Stripes (5050) an einem Shelly RGBW2 angeschlossen (jeweils die 2 Steuerleitungen eines Stripes an die 4 Output Kanäle des RGBW2) und diesen in "white Mode" gesetzt. Meine Idee ist die 2 Kanäle eines Stripes in einem fhem MQTT2_DEVICE zusammenzufassen.
Jetzt würde ich gerne in einem fhem device auch die Möglichkeit haben mit einem "on" Befehl die Kanäle 1 und 2 gleichzeitig einzuschalten.
Die Umgehungslösung über notify/doif funktioniert.
Schöner wäre jedoch auch diese Funktion im MQTT2_DEVICE abzubilden.
Dafür muss ich, soweit ich es weiß, bei der Original FW "nur" 2 Befehle senden.

Hat einer von euch eine Idee, wie ich das umsetzen kann?

Vielen Dank,

Gernot

Das hier funktioniert nicht:
defmod MQTT2_shellyrgbw2_6EAB2B MQTT2_DEVICE shellyrgbw2_6EAB2B
attr MQTT2_shellyrgbw2_6EAB2B IODev MQTT2
attr MQTT2_shellyrgbw2_6EAB2B autocreate 1
attr MQTT2_shellyrgbw2_6EAB2B comment Channel CW and WW for MQTT2_shellyrgbw2_6EAB2B, see also MQTT2_shellyrgbw2_6EAB2B_CH3
attr MQTT2_shellyrgbw2_6EAB2B genericDeviceType light
attr MQTT2_shellyrgbw2_6EAB2B icon light_control
attr MQTT2_shellyrgbw2_6EAB2B jsonMap brightness:pct
attr MQTT2_shellyrgbw2_6EAB2B model shelly2rgbw_4w_split
attr MQTT2_shellyrgbw2_6EAB2B readingList shellies/26_shellyrgbw2-6EAB2B_AB/white/1/status:.* {json2nameValue($EVENT,'ww_',$JSONMAP)}\
  shellies/26_shellyrgbw2-6EAB2B_AB/white/1:.* ww_state\
  shellies/26_shellyrgbw2-6EAB2B_AB/white/1/set:.* { json2nameValue($EVENT,'ww_',$JSONMAP) }\
  shellies/26_shellyrgbw2-6EAB2B_AB/white/0/status:.* {json2nameValue($EVENT,'cw_',$JSONMAP)}\
  shellies/26_shellyrgbw2-6EAB2B_AB/white/0:.* cw_state\
  shellies/26_shellyrgbw2-6EAB2B_AB/white/0/set:.* { json2nameValue($EVENT,'cw_',$JSONMAP) }\
  shellies/26_shellyrgbw2-6EAB2B_AB/status:.* { json2nameValue($EVENT, '', $JSONMAP) }\
  shellies/26_shellyrgbw2-6EAB2B_AB/announce:.* { json2nameValue($EVENT, '', $JSONMAP) }\
  shellies/26_shellyrgbw2-6EAB2B_AB/settings:.* { json2nameValue($EVENT, '', $JSONMAP) }
attr MQTT2_shellyrgbw2_6EAB2B room 99_MQTT2_DEVICE
attr MQTT2_shellyrgbw2_6EAB2B setList on:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/0/command on;;;;shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command on\
  off:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command on;;;;shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command off \
  cw_off:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/0/command off\
  cw_on:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/0/command on\
  cw_pct:colorpicker,BRI,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/0/set {"mode":"white","brightness":"$EVTPART1"}\
  cw_pct_on:colorpicker,BRI,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/0/set {"ison":"true","mode":"white","brightness":"$EVTPART1"}\
  ww_off:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command off\
  ww_on:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command on\
  ww_pct:colorpicker,CT,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/1/set {"mode":"white","brightness":"$EVTPART1"}\
  ww_pct_on:colorpicker,CT,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/1/set {"ison":"true","mode":"white","brightness":"$EVTPART1"}\
  x_update:noArg shellies/26_shellyrgbw2-6EAB2B_AB/command update_fw\
  x_mqttcom shellies/26_shellyrgbw2-6EAB2B_AB/command $EVTPART1\
  x_httpcom { X_PerformShellyHttpRequest($NAME,ReadingsVal($NAME,"ip","").",".$EVTPART1.",".ReadPassword($defs{$NAME})) }\
  x_httpcom_login {StorePassword($defs{$NAME},$EVTPART1) }\
  reboot:noArg { X_PerformShellyHttpRequest($NAME,ReadingsVal($NAME,"ip","").",".$EVENT.",".ReadPassword($defs{$NAME})) }\
  status:noArg { X_PerformShellyHttpRequest($NAME,ReadingsVal($NAME,"ip","").",".$EVENT.",".ReadPassword($defs{$NAME})) }\
  settings:noArg { X_PerformShellyHttpRequest($NAME,ReadingsVal($NAME,"ip","").",".$EVENT.",".ReadPassword($defs{$NAME})) }\
  shelly:noArg { X_PerformShellyHttpRequest($NAME,ReadingsVal($NAME,"ip","").",".$EVENT.",".ReadPassword($defs{$NAME})) }
attr MQTT2_shellyrgbw2_6EAB2B setStateList on off cw_on cw_off ww_on ww_off cw_pct_on ww_pct_on x_update x_mqttcom x_httpcom reboot status settings shelly
attr MQTT2_shellyrgbw2_6EAB2B webCmd on:off:ww_pct:cw_pct

genausowenig, wie mit dem Versuch es mit Perl:
...
attr MQTT2_shellyrgbw2_6EAB2B setList on:noArg { fhem("set $NAME cw_on");fhem("set $NAME ww_on")}
...
)

Der myUtils Code dazu:
##############################################
# $Id: myShellyUtils.pm supernova1963 $
#
# myUtils für shellies:
# http commnands an Shelly senden und Rückgabe json - string als sub topic /<httpCMND> des
# Shelly topics zu publizieren
# Bitte am "shelly MQTT2_DEVICE" das Attribut setList um folgende Zeile eränzen:
# x_httpCMND { X_PerformShellyHttpRequest($hash,<IP des Shelly>,$EVTPART1[,<username:password>]}
# für den Aufruf und der Auswertung des http commnads erweitern

package main;

use strict;
use warnings;
use POSIX;

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

# Enter you functions below _this_ line.

sub X_PerformShellyHttpRequest($$)
{
    my ($name, $shelly) = @_;
    my $hash = $defs{$name};
    my ($IP, $httpCMND, $httpCMND_Login) = split(/\,/,$shelly);
    my $shellyURL = "";
    if ($IP eq "" || !defined($IP))
    {
      Log3 $hash, 3, $hash->{NAME}.": Parameter IP ist nicht definiert!";
  return;
    }
    if ($httpCMND eq "" || !defined($httpCMND))
    {
      Log3 $hash, 3, $hash->{NAME}.": Parameter httpCMND ist nicht definiert!";
      return;
    }
    if ($httpCMND_Login eq "" || !defined($httpCMND_Login))
    {
      $shellyURL = "http://".$IP."/".$httpCMND;
    }
    else
    {
      if ( $httpCMND_Login =~ m/:/i)
      {
        $shellyURL = "http://".$httpCMND_Login."@".$IP."/".$httpCMND;
      }
      else
      {
        Log3 $hash, 3, $hash->{NAME}.": <user>:<password> nicht gesetzt oder fehlerhaft!";
        return;
      }
    }
  $hash->{helper}{httpCMND} = $httpCMND;
    my $param = {
                    url        => $shellyURL,
                    timeout    => 5,
                    hash       => $hash,                                                                                 # Muss gesetzt werden, damit die Callback funktion wieder $hash hat
                    method     => "GET",                                                                                 # Lesen von Inhalten
                    header     => "User-Agent: TeleHeater/2.2.3\r\nAccept: application/json",                            # Den Header gemäß abzufragender Daten ändern
                    callback   => \&X_ParseShellyHttpResponse                                                                  # Diese Funktion soll das Ergebnis dieser HTTP Anfrage bearbeiten
                };

    HttpUtils_NonblockingGet($param);                                                                                    # Starten der HTTP Abfrage. Es gibt keinen Return-Code.
}

sub X_ParseShellyHttpResponse($)
{
my ($param, $err, $data) = @_;
my $hash = $param->{hash};
my $name = $hash->{NAME};
my $rc = "";
my $httpCMND = $hash->{helper}{httpCMND};
my $IOdevice = InternalVal($name,"LASTInputDev","");
my $shellyID = ReadingsVal($name,"id","");
  if($err ne "")                                                                                                      # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist
  {
  Log3 $name, 3, "Fehler bei dem URL-Aufruf: ".$param->{url}." - $err";                                           # Eintrag fürs Log
    #readingsSingleUpdate($hash, "fullResponse", "ERROR", 0);                                                        # Readings erzeugen
$hash->{helper}{fullResponse} = "ERROR: ".$err;
  }
  elsif($data ne "")                                                                                                  # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes)
  {
    Log3 $name, 3, $param->{url}." returned: $data";                                                         # Eintrag fürs Log
# An dieser Stelle die Antwort parsen / verarbeiten mit $data
    #readingsSingleUpdate($hash, "fullResponse", $data, 0);
    my $json = new JSON;
    my $perl_scalar = $json->decode($data);
$hash->{helper}{fullResponse} = $json->pretty->encode($perl_scalar);
$rc = fhem("set ".$IOdevice." publish -r shellies/".$shellyID."/".$httpCMND." ".$data);
Log3 $name, 3, $name.": rc json2reading: ".json2reading($name, $data);
}
# Damit ist die Abfrage zuende.
   # Evtl. einen InternalTimer neu schedulen
}



sub StorePassword($$) {

    my ($hash, $password) = @_;
    my $index = $hash->{TYPE}."_".$hash->{NAME}."_passwd";
    my $key = getUniqueId().$index;
    my $enc_pwd = "";
my $name = $hash->{NAME};


    if(eval "use Digest::MD5;1")
    {
        $key = Digest::MD5::md5_hex(unpack "H*", $key);
        $key .= Digest::MD5::md5_hex($key);
    }

    for my $char (split //, $password)
    {

        my $encode=chop($key);
        $enc_pwd.=sprintf("%.2x",ord($char)^ord($encode));
        $key=$encode.$key;
    }

    my $err = setKeyValue($index, $enc_pwd);
   
if(defined($err)) {
Log3 $hash->{NAME}, 3, "$name: error while saving the password - $err";
return "error while saving the password - $err";
}
    Log3 $hash->{NAME}, 3, "$name: password successfully saved";
return "password successfully saved";
}

sub ReadPassword($) {

    my ($hash) = @_;
    my $name = $hash->{NAME};
    my $index = $hash->{TYPE}."_".$hash->{NAME}."_passwd";
    my $key = getUniqueId().$index;
    my ($password, $err);

    Log3 $name, 4, "($name) - Read password from file";

    ($err, $password) = getKeyValue($index);

    if ( defined($err) )
    {
      Log3 $name, 4, "($name) - unable to read password from file: $err";
      return undef;
    }
    if ( defined($password) )
    {
      if ( eval "use Digest::MD5;1" )
      {
        $key = Digest::MD5::md5_hex(unpack "H*", $key);
        $key .= Digest::MD5::md5_hex($key);
      }
      my $dec_pwd = '';
      for my $char (map { pack('C', hex($_)) } ($password =~ /(..)/g))
      {
        my $decode=chop($key);
        $dec_pwd.=chr(ord($char)^ord($decode));
        $key=$decode.$key;
      }
      return $dec_pwd;
    }
    else
    {
      Log3 $name, 4, "($name) - No password in file";
      return undef;
    }
}


1;
=pod
=item summary    Utilitiies for Shelly devices as MQTT2_CLIENT.
=item summary_DE Hilfsprogramme f&uuml;r Shellies, die als MQTT2_CLIENT definiert sind.
=begin html

<a name="myShellyUtils"></a>
<h3>myShellyUtils</h3>
Please look at German help <a href="commandref_de.html#mySHELLYUtils">myShellyUtils</a>

=end html
=begin html_DE

<a name="myShellyUtils"></a>

<h3>myShellyUtils</h3>

<h3>setlist Befehle</h3>
<ul>
  <code>X_PerformShellyHttpRequest(&lt;device&gt;,&lt;ip&gt;,&lt;command&gt;,&lt;user:password&gt;)</code>
  <br>
  Führt einen Shelly http Befehl aus und führt ein MQTT publish mit dem zurückgegebenen json - String aus.<br>
  Sollte der Shelly ein <b>RESTRICT LOGIN</b> besitzen, muss vorab einmalig der login zu diesem fhem device festgelegt werden.<br>

  <b>StorePassword</b>.
  <br>
  <code>set &lt;device name&gt; x_httpcom_login &lt;user:password&gt;</code><br>
  Dabei muss der user und das password dem "RESTRICT LOGIN" des Shelly entsprechen
  <br>
  Beispiele:<br>
  <ul>
   <code>attr device setlist settings:noArg { X_PerformShellyHttpRequest($NAME,ReadingsVal($NAME,"ip","").",".$EVTPART1.",".ReadPassword($defs{$NAME})) }</code><br>
  </ul>
</ul>
=end html_DE
=cut



P.S.: Mit tasmota hatte ich es bereits umgesetzt. Da gibt es den backlog command, mit dem ich 2 Befehle an den RGBW2 senden kann.

87insane

Schau mal in die mqtt2 attr Template Datei. Oder schau dir generell die templates mal an.

Variante A) eines der templates wird eh schon alles haben was du willst.

Variante B) du muss es selber leicht anpassen.

Denke aber das du dadurch alle deine Fragen beantwortet bekommst und das beste kommt noch - du erfährst noch mehr und entdeckst direkt neue Fragen ;)

Edit: ach ja und fürs Verständnis der templates ggf noch die Doku seitens shelly zum Thema mqtt. Da kannst du die setlist / resdinglist Befehle gut ableiten.

Gruß,
87insane

Gesendet von meinem LM-G810 mit Tapatalk

supernova1963

Vielen Dank für die Antwort, 87insane.

Ich habe nur das 4-Channel Shelly gefunden, dass kenne ich.
Auch das Suchen nach einem setlist befehl, der mehrere MQTT commands "published" habe ich in der gesamten Datei nicht gefunden.

Könntest du bitte etwas konkreter werden, ich weiß nicht, was du meinst?

Gernot

87insane

Es gibt mehrere setlist Kommandos in den ganzen templates bei denen auch mehrere bzw zusammen gesetzte Befehle gesendet werden.

An sich kannst du gerade bei dem shelly rgb2 Template einen mega zusammengebauten Befehl begutachten.

Dein Vorhaben hat bei mir, nachdem ich das nun 8 mal gelesen habe aber auch noch nicht ganz Klick gemacht. Kann auch sein das ich noch etwas falsch verstehe.

Gesendet von meinem LM-G810 mit Tapatalk


rudolfkoenig

MQTT2 kann nicht mit einem set Befehl mehrere topics publishen.
Um zwei Kanaele auf einmal zu schalten empfehle ich structure.
Als Hack gilt ein dummy mit notify und als ganz uebler Hack ein cmdalias :)

supernova1963

Nochmals, danke, 87insane

leider wird dieser Befehl (und es ist auch nur 1 publish command mit zusammengesetzten json als value) nur im color Mode vom Shelly RGBW2 unterstützt.

Ich möchte das für tasmota geflashten RGBW2 umgesetzte mit der originalen FW realisieren (https://www.shelly-support.eu/forum/index.php?thread/2306-meine-umsetzung-eines-rgbw2-mit-2-led-stripes-5050-ww-cw/&postID=28532#post28532)

supernova1963


87insane

@rudolfkoenig: Jetzt mal ganz grundlegend... Warum nicht?
Ich meine wenn ich im Perl Zweig dahinter einfach zwei Befehle mit einem set absetze, ist das zwar gefudelt aber müsste doch klappen?

Mal eben kurz aus einem meiner Fudel Geräte rauß gekramt...
setList switch:on,off,refresh {fhem("set wol_kai_pc $EVTPART1")}
Im Perl Part muss das doch möglich sein, einfach den zweiten Befehl dahinter zu schieben? (Gefudelt aber finde ich besser als x weitere Geräte).

EDIT: Das natürlich mit publish Befehlen von mqtt.....

rudolfkoenig

ZitatIch meine wenn ich im Perl Zweig dahinter einfach zwei Befehle mit einem set absetze, ist das zwar gefudelt aber müsste doch klappen?
Ja, sollte tun, ist in meinen Augen die gleiche Hack-Klasse wie cmdalias.

87insane

Oh! Ganz übel! Ich las davon.

Spaß bei Seite. In solchen Momenten freunde ich mich mit FHEM meist wieder an, wenn ich sauer bin. Ich mag es nicht wenn ich für "Eine Sache" x-Geräte haben muss. Im Bereich von MQTT kann man sich super viel selber "hacken". Dazu noch einfach in einer Zeile und auch in dem Gerät wo man das braucht. Mit cmdalias kam ich z.B. noch nie klar :(

Aber okay. Es geht also über viele Wege, wie immer in FHEM ;) Welche wäre am Ende aus Programmierer-Sicht die Beste (Zeit/Ressorcen)? Ich meine wir reden über nichts wildes aber immer wenn ich mal in einem "nicht MQTT" Gerät unterwegs bin, wird es meist komplizierter als sonst. Bei MQTT kann ich in FHEM auch direkt in den Readings, Dinge hinterlegen und mir alles zurecht biegen. Das weiß Du sicher alles aber da bin ich auch echt neugierig wie Du das siehst!

supernova1963

Ich habe folgendes versucht:

attr MQTT2_shellyrgbw2_6EAB2B setList on:noArg { fhem("set $NAME cw_on");;;;fhem("set $NAME ww_on")} \
  off:noArg { fhem("set $NAME cw_off");;;;fhem("set $NAME ww_off")} \
  cw_off:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/0/command off\
  cw_on:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/0/command on\
  cw_pct:colorpicker,BRI,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/0/set {"mode":"white","brightness":"$EVTPART1"}\
  cw_pct_on:colorpicker,BRI,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/0/set {"ison":"true","mode":"white","brightness":"$EVTPART1"}\
  ww_off:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command off\
  ww_on:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command on\
  ww_pct:colorpicker,CT,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/1/set {"mode":"white","brightness":"$EVTPART1"}\
  ww_pct_on:colorpicker,CT,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/1/set {"ison":"true","mode":"white","brightness":"$EVTPART1"}\



Das Ergebnis:

2020.03.24 20:18:51 3 : MQTT2_DEVICE set MQTT2_shellyrgbw2_6EAB2B on
2020-03-24 20:18:51 MQTT2_DEVICE MQTT2_shellyrgbw2_6EAB2B set_on
2020.03.24 20:18:51 3 : Bad regexp: Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/^MQTT2:{ <-- HERE :.*$/ at ./FHEM/10_MQTT2_DEVICE.pm line 546.


Was mache ich falsch?

supernova1963

Auch folgendes funktioniert nicht:

attr MQTT2_shellyrgbw2_6EAB2B setList on:noArg { fhem("set $NAME shellies/26_shellyrgbw2-6EAB2B_AB/white/0/command on");;fhem("set $NAME shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command on")}  \
  off:noArg { fhem("set $NAME shellies/26_shellyrgbw2-6EAB2B_AB/white/0/command off");;fhem("set $NAME shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command off")} \
  cw_off:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/0/command off\
  cw_on:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/0/command on\
  cw_pct:colorpicker,BRI,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/0/set {"mode":"white","brightness":"$EVTPART1"}\
  cw_pct_on:colorpicker,BRI,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/0/set {"ison":"true","mode":"white","brightness":"$EVTPART1"}\
  ww_off:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command off\
  ww_on:noArg shellies/26_shellyrgbw2-6EAB2B_AB/white/1/command on\
  ww_pct:colorpicker,CT,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/1/set {"mode":"white","brightness":"$EVTPART1"}\
  ww_pct_on:colorpicker,CT,0,1,100 shellies/26_shellyrgbw2-6EAB2B_AB/white/1/set {"ison":"true","mode":"white","brightness":"$EVTPART1"}\



Ergebnis:

2020-03-24 20:38:16 MQTT2_DEVICE MQTT2_shellyrgbw2_6EAB2B set_on
2020.03.24 20:38:16 3 : Bad regexp: Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/^MQTT2:{ <-- HERE :.*$/ at ./FHEM/10_MQTT2_DEVICE.pm line 546.

rudolfkoenig

Die "Bad regexp" Zeile hat mit einem readingList was zu tun.
Ich habe gerade die Meldung erweitert, damit man auch das problematische Regexp sieht.

87insane

Bin gespannt was morgen hier steht. Du muss auch nicht doppelt in fhem("") gehen. Macht es ggf einfacher.

Bist aber in meinen Augen nah dran. Morgen gucke ich wieder nach und helfe fleißig :)

Edit: mich würde noch interessieren woher du kopierst..sieht aus wie aus einem Template. Wenn du das so in die Liste kopierst, wird es nicht gehen können.

Gesendet von meinem LM-G810 mit Tapatalk

hexenmeister

Wenn es nicht unbedingt MQTT2_DEVICE sein muss, dann kann MQTT_GENERIC_BRIDGE ein event (aus einem Reading) auf zwei Topics posten.

in etwa so:

attr <device name> mqttPublish state:topic=topic1 state!2:topic=topic2