[gelöst]Experte f. websocket&Perl u./o. Python zwecks Übersetzung für fhem

Begonnen von KölnSolar, 07 Oktober 2018, 14:07:45

Vorheriges Thema - Nächstes Thema

KölnSolar

Hallo Ihr Lieben,
ins STV-Modul haben wir für Samsung-Fernseher ein Python-Skript eingebunden. Aus versch. Gründen würde ich das gerne direkt im Modul also in Perl mit websocket umsetzen. Python u. websocket sind mir aber völlig fremd.  :'( Ich poste mal den Extrakt des Python-Skripts, einen funktionieren Perl-websocket-Zugriff und meine leider nicht funktionierende Ableitung:
Python-Skript
URL_FORMAT = "ws://{}:{}/api/v2/channels/samsung.remote.control?name={}"

class RemoteWebsocket():
    """Object for remote control connection."""

    def __init__(self, config):
.
.
        url = URL_FORMAT.format(config["host"], config["port"],
                                self._serialize_string(config["name"]))

        self.connection = websocket.create_connection(url, config["timeout"])

        self._read_response()
.
.
    def control(self, key):
        """Send a control command."""
.
.
        payload = json.dumps({
            "method": "ms.remote.control",
            "params": {
                "Cmd": "Click",
                "DataOfCmd": key,
                "Option": "false",
                "TypeOfRemote": "SendRemoteKey"
            }
        })

       self.connection.send(payload)


Beispiel eines vergleichbaren websocket-Zugriffs aus dem STV_Modul
if( my $socket = IO::Socket::INET->new(PeerAddr=>"$host:$port", Timeout=>2, Blocking=>1, ReuseAddr=>1) ) {
$ret = "GET $path HTTP/1.1\r\n";
my $webclient_header = 'Upgrade: websocket' . "\r\n" .
        'Connection: Upgrade' . "\r\n" .
'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' . "\r\n" .
'Sec-WebSocket-Version: 13' . "\r\n\r\n";
syswrite $socket, $ret . $webclient_header;
my $buf; sysread $socket, $buf, 10240; Log3 $name, 5, "[STV] $name: $buf";
$buf =~ /^[^ ]+ ([\d]{3})/;
if ($1 eq '101') {
.
.



mein erfolgloser Versuch
if(my $socket = IO::Socket::INET->new(PeerAddr=>"$hash->{Host}:$hash->{Port}", Timeout=>2, Blocking=>1, ReuseAddr=>1)){
my $ret = "GET ws://$hash->{Host}:$hash->{Port}/api/v2/channels/samsung.remote.control?name=RkhFTXJlbW90ZQ== HTTP/1.1\r\n";
my $webclient_header = 'Upgrade: websocket' . "\r\n" .
'Connection: Upgrade' . "\r\n" .
'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' . "\r\n" .
'Sec-WebSocket-Version: 13' . "\r\n\r\n";
Log3 $name, 5, "[STV] writeto $name: " . $ret . $webclient_header;
syswrite $socket, $ret . $webclient_header;
Log3 $name, 5, "[STV] writeto $name, return code: $!" if (!$1);
my $buf;
sysread $socket, $buf, 10240;
Log3 $name, 5, "[STV] read $name, return code: $!" if (!$1);
Log3 $name, 5, "[STV] readbuffer $name: $buf";
my $payload = JSON::encode_json({
"method" => "ms.remote.control",
"params" => {
"Cmd" => "Click",
"DataOfCmd" => "KEY_".$ARGV[$argnum],
"Option" => "false",
"TypeOfRemote" => "SendRemoteKey"
} });
Log3 $name, 5, "[STV] payload of command to $name: " . $payload;
syswrite $socket, $payload;     
Log3 $name, 5, "[STV] write command to $name, return code: $!" if (!$1);
shutdown $socket, 2;
}
   
Logoutput
2018.10.07 13:35:48 5: [STV] writeto Fernseher: GET ws://192.168.178.62:8001/api/v2/channels/samsung.remote.control?name=RkhFTVJlbW90ZQ== HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13


2018.10.07 13:35:48 5: [STV] writeto Fernseher, return code:
2018.10.07 13:35:48 5: [STV] read Fernseher, return code:
2018.10.07 13:35:48 5: [STV] readbuffer Fernseher:
2018.10.07 13:35:48 5: [STV] payload of command to Fernseher: {"params":{"TypeOfRemote":"SendRemoteKey","Option":"false","Cmd":"Click","DataOfCmd":"KEY_0"},"method":"ms.remote.control"}
2018.10.07 13:35:48 5: [STV] write command to Fernseher, return code:

Der socketaufbau scheint zu klappen, URL f. das GET ist wie im Ursprungs-Python-Skript(das ist im Perl-websocket-Beispiel mit $path etwas anders), $payload scheint richtig, aber bei syswrite/sysread ist $1 immer undefined(also Fehler ?) aber $! ist überraschenderweise leer. Wie komme ich an den Fehler ?

Frage ich die falschen Variablen ab ?  :-[ :-\ :-[

Nehme ich besser print/write/read ?

Gibt es evtl. einen besseren Ansatz für den websocket-Aufbau und das übertragen des Befehls ?

Danke&Grüße
Markus

RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

rudolfkoenig

Websocket ist eine  HTTP Verbindung, wo beim ersten GET per HTTP Header "Connection:Updrade" signalisiert wird, dass man eine dauerhafte Verbindung haben will. Der Server anwortet mit 101 Switching Protocols, und einigen weiteren Headerzeilen, wie dem Sec-WebSocket-Accept Header, was den SHA1 hash von ebenfalls gesendeten Sec-WebSocket-Key und einen bekannten UUID enthaelt. Die Daten kommen danach als <Laenge>+Daten, wobei Laenge je nach Wert unterschiedlich kodiert ist. Eine Implementation ist in FHEMWEB.pm zu finden, allerdings nicht sauber getrennt als Bibliothek. Wenn das mehrere Module verwenden wollen, dann sollten wir ueber eine Auslagerung als Bibliothek nachdenken.
P.S. In deinem Aufruf ist hinter GET die Angabe von ws:// und hostname definitiv falsch, beides wird nicht benoetigt. Evtl. auch Weiteres, das kann ich nicht auf die Schnelle beurteilen.

KölnSolar

Danke Rudi,
ZitatIn deinem Aufruf ist hinter GET die Angabe von ws:// und hostname definitiv falsch
das hatte ich mir fast gedacht, weil es in dem funktionierenden Beispiel auch ohne ws://Host:Port ist. Ich habs aber auch schon erfolglos ohne probiert.  :'(
Ich guck mir mal FHEMWEB an, ob ich da was verstehe, was ich ausprobieren kann.
RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

KölnSolar

ein Schrittchen weiter.  ;D
Ich habe das Python-websocket-Skript etwas gesprächiger gemacht
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13


2018.10.07 19:12:18 5: [STV] writeto Fernseher, return code:
2018.10.07 19:12:18 5: [STV] read Fernseher, return code:
2018.10.07 19:12:18 5: [STV] readbuffer Fernseher:
2018.10.07 19:12:18 5: [STV] payload of command to Fernseher: {"method":"ms.remote.control","params":{"Cmd":"Click","Option":"false","DataOfCmd":"KEY_CH_LIST","TypeOfRemote":"SendRemoteKey"}}
2018.10.07 19:12:18 5: [STV] write command to Fernseher, return code:
Create Websocket with url: ws://192.168.178.62:8001/api/v2/channels/samsung.remote.control?name=RkhFTVJlbW90ZQ==
--- request header ---
--- request header ---
GET /api/v2/channels/samsung.remote.control?name=RkhFTVJlbW90ZQ== HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 192.168.178.62:8001
Origin: http://192.168.178.62:8001
Sec-WebSocket-Key: wecx1rcMFfF2/BFbQKOCcQ==
Sec-WebSocket-Version: 13


GET /api/v2/channels/samsung.remote.control?name=RkhFTVJlbW90ZQ== HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 192.168.178.62:8001
Origin: http://192.168.178.62:8001
Sec-WebSocket-Key: wecx1rcMFfF2/BFbQKOCcQ==
Sec-WebSocket-Version: 13


-----------------------
-----------------------
--- response header ---
--- response header ---
HTTP/1.1 101 Switching Protocols
HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Upgrade: WebSocket
Connection: Upgrade
Connection: Upgrade
Sec-WebSocket-Accept: oFrxN3CwSHI9YwhMloZuniiqk1c=
Sec-WebSocket-Accept: oFrxN3CwSHI9YwhMloZuniiqk1c=
-----------------------
-----------------------
Sending Payload: {"params": {"TypeOfRemote": "SendRemoteKey", "Cmd": "Click", "DataOfCmd": "KEY_CH_LIST", "Option": "false"}, "method": "ms.remote.control"}
Sending control command: KEY_CH_LIST
send: '\x81\xfe\x00\x8b\xed\xfe\xaf\x1b\x96\xdc\xdfz\x9f\x9f\xc2h\xcf\xc4\x8f`\xcf\xaa\xd6k\x88\xb1\xc9I\x88\x93\xc0o\x88\xdc\x95;\xcf\xad\xcau\x89\xac\xcav\x82\x8a\xcaP\x88\x87\x8d7\xcd\xdc\xecv\x89\xdc\x95;\xcf\xbd\xc3r\x8e\x95\x8d7\xcd\xdc\xebz\x99\x9f\xe0}\xae\x93\xcb9\xd7\xde\x8dP\xa8\xa7\xf0X\xa5\xa1\xe3R\xbe\xaa\x8d7\xcd\xdc\xe0k\x99\x97\xc0u\xcf\xc4\x8f9\x8b\x9f\xc3h\x88\xdc\xd27\xcd\xdc\xc2~\x99\x96\xc0\x7f\xcf\xc4\x8f9\x80\x8d\x81i\x88\x93\xc0o\x88\xd0\xcct\x83\x8a\xddt\x81\xdc\xd2'
send: '\x81\xfe\x00\x8b\xed\xfe\xaf\x1b\x96\xdc\xdfz\x9f\x9f\xc2h\xcf\xc4\x8f`\xcf\xaa\xd6k\x88\xb1\xc9I\x88\x93\xc0o\x88\xdc\x95;\xcf\xad\xcau\x89\xac\xcav\x82\x8a\xcaP\x88\x87\x8d7\xcd\xdc\xecv\x89\xdc\x95;\xcf\xbd\xc3r\x8e\x95\x8d7\xcd\xdc\xebz\x99\x9f\xe0}\xae\x93\xcb9\xd7\xde\x8dP\xa8\xa7\xf0X\xa5\xa1\xe3R\xbe\xaa\x8d7\xcd\xdc\xe0k\x99\x97\xc0u\xcf\xc4\x8f9\x8b\x9f\xc3h\x88\xdc\xd27\xcd\xdc\xc2~\x99\x96\xc0\x7f\xcf\xc4\x8f9\x80\x8d\x81i\x88\x93\xc0o\x88\xd0\xcct\x83\x8a\xddt\x81\xdc\xd2'
send: '\x88\x82\xe0\x1d\x7f~\xe3\xf5'
send: '\x88\x82\xe0\x1d\x7f~\xe3\xf5'


also zusätzlich die beiden Header-Zeilen Host u. Origin. Der Sec-WebSocket-Key anders, aber der ist ja nur zur Prüfung der Rückmeldung. Eingebaut und ich bekomme tatsächlich die erhoffte Antwort


HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=



t.b.c.
RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

KölnSolar

Es ist vollbracht.  ;D ;D ;D ;D ;D

Es brauchte die zusätzliche Hostzeile, damit der TV antwortet. Und den Json-Payload musste ich noch durch die selbe Routine jagen, die bereits in oben mit "Beispiel eines vergleichbaren websocket-Zugriffs aus dem STV_Modul" enthalten ist.

RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt