я новичок в Python и буду конвертировать PHP-скрипт в Python. Он подключает мой LG SmartTV к моему Raspi, но есть проблема с функцией подключения
Оригинальный код php
function connect()
{
$ws_handshake_cmd = "GET " . $this->path . " HTTP/1.1\r\n";
$ws_handshake_cmd.= "Upgrade: websocket\r\n";
$ws_handshake_cmd.= "Connection: Upgrade\r\n";
$ws_handshake_cmd.= "Sec-WebSocket-Version: 13\r\n";
$ws_handshake_cmd.= "Sec-WebSocket-Key: " . $this->ws_key . "\r\n";
$ws_handshake_cmd.= "Host: ".$this->host.":".$this->port."\r\n\r\n";
$this->sock = fsockopen($this->host, $this->port, $errno, $errstr, 2);
socket_set_timeout($this->sock, 0, 10000);
echo "Sending WS handshake\n$ws_handshake_cmd\n";
$response = $this->send($ws_handshake_cmd);
if ($response)
{
echo "WS Handshake Response:\n$response\n";
} else
echo "ERROR during WS handshake!\n";
preg_match('#Sec-WebSocket-Accept:\s(.*)$#mU', $response, $matches);
if ($matches) {
$keyAccept = trim($matches[1]);
$expectedResonse = base64_encode(pack('H*', sha1($this->ws_key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$this->connected = ($keyAccept === $expectedResonse) ? true : false;
} else $this->connected=false;
if ($this->connected) echo "Sucessfull WS connection to $this->host:$this->port\n\n";
return $this->connected;
}
function lg_handshake()
{
if (!$this->connected) $this->connect();
if ($this->connected)
{
$handshake = "{"type":"register","id":"register_0","payload":{"forcePairing":false,"pairingType":"PROMPT","client-key":"HANDSHAKEKEYGOESHERE","manifest":{"manifestVersion":1,"appVersion":"1.1","signed":{"created":"20140509","appId":"com.lge.test","vendorId":"com.lge","localizedAppNames":{"":"LG Remote App","ko-KR":"리모컨 앱","zxx-XX":"ЛГ R�мot� AПП"},"localizedVendorNames":{"":"LG Electronics"},"permissions":["TEST_SECURE","CONTROL_INPUT_TEXT","CONTROL_MOUSE_AND_KEYBOARD","READ_INSTALLED_APPS","READ_LGE_SDX","READ_NOTIFICATIONS","SEARCH","WRITE_SETTINGS","WRITE_NOTIFICATION_ALERT","CONTROL_POWER","READ_CURRENT_CHANNEL","READ_RUNNING_APPS","READ_UPDATE_INFO","UPDATE_FROM_REMOTE_APP","READ_LGE_TV_INPUT_EVENTS","READ_TV_CURRENT_TIME"],"serial":"2f930e2d2cfe083771f68e4fe7bb07"},"permissions":["LAUNCH","LAUNCH_WEBAPP","APP_TO_APP","CLOSE","TEST_OPEN","TEST_PROTECTED","CONTROL_AUDIO","CONTROL_DISPLAY","CONTROL_INPUT_JOYSTICK","CONTROL_INPUT_MEDIA_RECORDING","CONTROL_INPUT_MEDIA_PLAYBACK","CONTROL_INPUT_TV","CONTROL_POWER","READ_APP_STATUS","READ_CURRENT_CHANNEL","READ_INPUT_DEVICE_LIST","READ_NETWORK_STATE","READ_RUNNING_APPS","READ_TV_CHANNEL_LIST","WRITE_NOTIFICATION_TOAST","READ_POWER_STATE","READ_COUNTRY_INFO"],"signatures":[{"signatureVersion":1,"signature":"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw=="}]}}}";
if (isset($this->lg_key))
$handshake = str_replace('HANDSHAKEKEYGOESHERE',$this->lg_key,$handshake);
else $handshake = "{"type":"register","id":"register_0","payload":{"forcePairing":false,"pairingType":"PROMPT","manifest":{"manifestVersion":1,"appVersion":"1.1","signed":{"created":"20140509","appId":"com.lge.test","vendorId":"com.lge","localizedAppNames":{"":"LG Remote App","ko-KR":"리모컨 앱","zxx-XX":"ЛГ R�мot� AПП"},"localizedVendorNames":{"":"LG Electronics"},"permissions":["TEST_SECURE","CONTROL_INPUT_TEXT","CONTROL_MOUSE_AND_KEYBOARD","READ_INSTALLED_APPS","READ_LGE_SDX","READ_NOTIFICATIONS","SEARCH","WRITE_SETTINGS","WRITE_NOTIFICATION_ALERT","CONTROL_POWER","READ_CURRENT_CHANNEL","READ_RUNNING_APPS","READ_UPDATE_INFO","UPDATE_FROM_REMOTE_APP","READ_LGE_TV_INPUT_EVENTS","READ_TV_CURRENT_TIME"],"serial":"2f930e2d2cfe083771f68e4fe7bb07"},"permissions":["LAUNCH","LAUNCH_WEBAPP","APP_TO_APP","CLOSE","TEST_OPEN","TEST_PROTECTED","CONTROL_AUDIO","CONTROL_DISPLAY","CONTROL_INPUT_JOYSTICK","CONTROL_INPUT_MEDIA_RECORDING","CONTROL_INPUT_MEDIA_PLAYBACK","CONTROL_INPUT_TV","CONTROL_POWER","READ_APP_STATUS","READ_CURRENT_CHANNEL","READ_INPUT_DEVICE_LIST","READ_NETWORK_STATE","READ_RUNNING_APPS","READ_TV_CHANNEL_LIST","WRITE_NOTIFICATION_TOAST","READ_POWER_STATE","READ_COUNTRY_INFO"],"signatures":[{"signatureVersion":1,"signature":"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw=="}]}}}";
echo "Sending LG handshake\n$handshake\n";
$response = $this->send(hybi10Encode($handshake));
if ($response)
{
echo "\nLG Handshake Response\n".json_string($response)."\n";
$result = json_array($response);
if ($result && array_key_exists('id',$result) && $result['id']=='result_0' && array_key_exists('client-key',$result['payload']))
{
// LG client-key received: COMPARE!!!
if ($this->lg_key == $result['payload']['client-key'])
echo "LG Client-Key successfully approved\n";
} else if ($result && array_key_exists('id',$result) && $result['id']=='register_0' && array_key_exists('pairingType',$result['payload']) && array_key_exists('returnValue',$result['payload']))
{ // LG TV is prompting for access rights
if ($result['payload']['pairingType'] == "PROMPT" && $result['payload']['returnValue'] == "true")
{
$starttime = microtime(1);
$lg_key_received = false;
$error_received = false;
do {
$response = @fread($this->sock, 8192);
$result = json_array($response);
if ($result && array_key_exists('id',$result) && $result['id']=='register_0' && is_array($result['payload']) && array_key_exists('client-key',$result['payload']))
{
$lg_key_received = true;
$this->lg_key = $result['payload']['client-key'];
echo "LG Client-Key successfully received: $this->lg_key\n";
} else if ($result && array_key_exists('id',$result) && $result['id']=='register_0' && array_key_exists('error',$result))
{
$error_received = true;
echo "ERROR: ".$result['error']."\n";
}
usleep(200000);
$time = microtime(1);
} while ($time-$starttime<60 && !$lg_key_received && !$error_received);
}
}
} else echo "ERROR during LG handshake:\n";
} else return FALSE;
}
function disconnect()
{
$this->connected=false;
@fclose($this->sock);
echo "Connection closed to $this->host\n";
}
function send($msg)
{
@fwrite($this->sock, $msg);
usleep(250000);
$response = @fread($this->sock, 8192);
return $response;
}
function send_command($cmd)
{
if (!$this->connected) $this->connect();
if ($this->connected)
{
echo "Sending command : $cmd\n";
$response = $this->send(hybi10Encode($cmd));
if ($response)
echo "Command response : ".json_string($response)."\n";
else
echo "Error sending command: $cmd\n";
return $response;
}
}
function message($msg)
{
$command = "{"id":"message","type":"request","uri":"ssap://system.notifications/createToast","payload":{"message": "$msg"}}";
$this->send_command($command);
}
function power_off()
{
$command = "{"id":"power_off","type":"request","uri":"ssap://system/turnOff"}";
$this->send_command($command);
}
function set_volume($vol)
{
$command = "{"id":"set_volume","type":"request","uri":"ssap://audio/setVolume","payload":{"volume":$vol}}";
$this->send_command($command);
}
}
function hybi10Encode($payload, $type = 'text', $masked = true) {
$frameHead = array();
$frame = '';
$payloadLength = strlen($payload);
switch ($type) {
case 'text':
// first byte indicates FIN, Text-Frame (10000001):
$frameHead[0] = 129;
break;
case 'close':
// first byte indicates FIN, Close Frame(10001000):
$frameHead[0] = 136;
break;
case 'ping':
// first byte indicates FIN, Ping frame (10001001):
$frameHead[0] = 137;
break;
case 'pong':
// first byte indicates FIN, Pong frame (10001010):
$frameHead[0] = 138;
break;
}
// set mask and payload length (using 1, 3 or 9 bytes)
if ($payloadLength > 65535) {
$payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 255 : 127;
for ($i = 0; $i < 8; $i++) {
$frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
}
// most significant bit MUST be 0 (close connection if frame too big)
if ($frameHead[2] > 127) {
$this->close(1004);
return false;
}
} elseif ($payloadLength > 125) {
$payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 254 : 126;
$frameHead[2] = bindec($payloadLengthBin[0]);
$frameHead[3] = bindec($payloadLengthBin[1]);
} else {
$frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
}
// convert frame-head to string:
foreach (array_keys($frameHead) as $i) {
$frameHead[$i] = chr($frameHead[$i]);
}
if ($masked === true) {
// generate a random mask:
$mask = array();
for ($i = 0; $i < 4; $i++) {
$mask[$i] = chr(rand(0, 255));
}
$frameHead = array_merge($frameHead, $mask);
}
$frame = implode('', $frameHead);
// append payload to frame:
for ($i = 0; $i < $payloadLength; $i++) {
$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
}
return $frame;
}
мой код на питоне
def connect(self):
ws_handshake_cmd = "GET "+str(self._path)+" HTTP/1.1\r\n"ws_handshake_cmd += "Upgrade: websocket\r\n"ws_handshake_cmd += "Connection: Upgrade\r\n"ws_handshake_cmd += "Sec-WebSocket-Version: 13\r\n"ws_handshake_cmd += "Sec-WebSocket-Key: "+ str(self._wskey) + "\r\n"ws_handshake_cmd += "Host: "+ str(self._host)+":"+str(self._port)+"\r\n\r\n"
try:
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._sock.connect((self._host, self._port))
#self._sock.settimeout(20)
except:
print("Verbindung zu ", str(self._host), "nicht möglich!")
print("Sending WS handshake", ws_handshake_cmd)
response = self.send(ws_handshake_cmd.encode('utf-8'))
if response == '':
print("ERROR during WS handshake!")
else:
print("WS Handshake Response:", response)
matches = re.search('Sec-WebSocket-Accept:\s*(.*=)', response)
returnkey = matches.group().strip()
#print("Match in response ",keyAccept)
tvkey = '' # von TV 28 stellen
wskey = str(self._wskey).encode('utf8') #zu TV 26 stellen
key = str('258EAFA5-E914-47DA-95CA-C5AB0DC85B11').encode('utf8')
keys = str("%s%s" % (wskey , key)).encode('utf8')
hash = hashlib.sha1(keys).hexdigest()
print(hash)
asci = bytes(hash.encode('utf-8'))
print(asci)
print(base64.standard_b64encode(asci))
expectedResonse = base64.b64encode(asci)
print("gerechneter wert",expectedResonse)
if returnkey == expectedResonse:
self._connected = True
print("Key Akzeptiert")
else:
self._connected = False
print("Key nicht Akzeptiert")
if self._connected:
print( "Sucessfull WS connection to", self._host, ": ", self._port)
return self._connected
def lg_handshake(self):
if self._connected == False:
self.connect()
if self._connected:
handshake ='{"type":"register","id":"register_0","payload":{"forcePairing":False,"pairingType":"PROMPT","client-key":"'+str(self._wskey)+'manifest":{"manifestVersion":1,"appVersion":"1.1","signed":{"created":"20140509","appId":"com.lge.test","vendorId":"com.lge","localizedAppNames":{"":"LG Remote App","ko-KR":"리모컨 앱","zxx-XX":"ЛГ RÑ?мotÑ? AПП"},"localizedVendorNames":{"":"LG Electronics"},"permissions":["TEST_SECURE","CONTROL_INPUT_TEXT","CONTROL_MOUSE_and_KEYBOARD","READ_INSTALLED_APPS","READ_LGE_SDX","READ_NOTIFICATIONS","SEARCH","WRITE_SETTINGS","WRITE_NOTIFICATION_ALERT","CONTROL_POWER","READ_CURRENT_CHANNEL","READ_RUNNING_APPS","READ_UPDATE_INFO","UPDATE_FROM_REMOTE_APP","READ_LGE_TV_INPUT_EVENTS","READ_TV_CURRENT_TIME"],"serial":"2f930e2d2cfe083771f68e4fe7bb07"},"permissions":["LAUNCH","LAUNCH_WEBAPP","APP_TO_APP","CLOSE","TEST_OPEN","TEST_PROTECTED","CONTROL_AUDIO","CONTROL_DISPLAY","CONTROL_INPUT_JOYSTICK","CONTROL_INPUT_MEDIA_RECORDING","CONTROL_INPUT_MEDIA_PLAYBACK","CONTROL_INPUT_TV","CONTROL_POWER","READ_APP_STATUS","READ_CURRENT_CHANNEL","READ_INPUT_DEVICE_LIST","READ_NETWORK_STATE","READ_RUNNING_APPS","READ_TV_CHANNEL_LIST","WRITE_NOTIFICATION_TOAST","READ_POWER_STATE","READ_COUNTRY_INFO"],"signatures":[{"signatureVersion":1,"signature":"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw=="}]}}}'
if self._tvkey != '':
handshake = str.replace(self._handshakecode,self._tvkey)
else:
handshake = '{"type":"register","id":"register_0","payload":{"forcePairing":False,"pairingType":"PROMPT","manifest":{"manifestVersion":1,"appVersion":"1.1","signed":{"created":"20140509","appId":"com.lge.test","vendorId":"com.lge","localizedAppNames":{"":"LG Remote App","ko-KR":"리모컨 앱","zxx-XX":"ЛГ RÑ?мotÑ? AПП"},"localizedVendorNames":{"":"LG Electronics"},"permissions":["TEST_SECURE","CONTROL_INPUT_TEXT","CONTROL_MOUSE_and_KEYBOARD","READ_INSTALLED_APPS","READ_LGE_SDX","READ_NOTIFICATIONS","SEARCH","WRITE_SETTINGS","WRITE_NOTIFICATION_ALERT","CONTROL_POWER","READ_CURRENT_CHANNEL","READ_RUNNING_APPS","READ_UPDATE_INFO","UPDATE_FROM_REMOTE_APP","READ_LGE_TV_INPUT_EVENTS","READ_TV_CURRENT_TIME"],"serial":"2f930e2d2cfe083771f68e4fe7bb07"},"permissions":["LAUNCH","LAUNCH_WEBAPP","APP_TO_APP","CLOSE","TEST_OPEN","TEST_PROTECTED","CONTROL_AUDIO","CONTROL_DISPLAY","CONTROL_INPUT_JOYSTICK","CONTROL_INPUT_MEDIA_RECORDING","CONTROL_INPUT_MEDIA_PLAYBACK","CONTROL_INPUT_TV","CONTROL_POWER","READ_APP_STATUS","READ_CURRENT_CHANNEL","READ_INPUT_DEVICE_LIST","READ_NETWORK_STATE","READ_RUNNING_APPS","READ_TV_CHANNEL_LIST","WRITE_NOTIFICATION_TOAST","READ_POWER_STATE","READ_COUNTRY_INFO"],"signatures":[{"signatureVersion":1,"signature":"eyJhbGdvcml0aG0iOiJSU0EtU0hBMjU2Iiwia2V5SWQiOiJ0ZXN0LXNpZ25pbmctY2VydCIsInNpZ25hdHVyZVZlcnNpb24iOjF9.hrVRgjCwXVvE2OOSpDZ58hR+59aFNwYDyjQgKk3auukd7pcegmE2CzPCa0bJ0ZsRAcKkCTJrWo5iDzNhMBWRyaMOv5zWSrthlf7G128qvIlpMT0YNY+n/FaOHE73uLrS/g7swl3/qH/BGFG2Hu4RlL48eb3lLKqTt2xKHdCs6Cd4RMfJPYnzgvI4BNrFUKsjkcu+WD4OO2A27Pq1n50cMchmcaXadJhGrOqH5YmHdOCj5NSHzJYrsW0HPlpuAx/ECMeIZYDh6RMqaFM2DXzdKX9NmmyqzJ3o/0lkk/N97gfVRLW5hA29yeAwaCViZNCP8iC9aO0q9fQojoa7NQnAtw=="}]}}}'
print("Sending LG handshake",handshake)
response = self.send(self.hybi10Encode(handshake))
if response == True:
print("LG Handshake Response",self.json_string(response))
result = self.json_array(response)
if result and ('id' in result) and result['id']=='result_0' and ('client-key' in result['payload']):
#// LG client-key received: COMPARE!!!
if self._lg_key == result['payload']['client-key']:
print("LG Client-Key successfully approved")
else:
if result and ('id'in result) and result['id']=='register_0' and ('pairingType' in result['payload']) and ('returnValue' in result['payload']):
#// LG TV is prompting for access rights
if (result['payload']['pairingType'] == 'PROMPT') and (result['payload']['returnValue'] == 'True'):
starttime = time.time()
lg_key_received = False
error_received = False
while (time-starttime<60 and not lg_key_received and not error_received):
response = self._sock.read(8192)
result = self.json_array(response)
if result == True and ('id' in result) and result['id']=='register_0' and is_array(result['payload']) and ('client-key' in result['payload']):
lg_key_received = True
self._lg_key = result['payload']['client-key']
print("LG Client-Key successfully received:",self._lg_key)
elif result and ('id' in result) and result['id']=='register_0' and ('error' in result):
error_received = True
print("ERROR: ",result['error'])
time.sleep(200000 / 1000000.0)#usleep(200000)
time = time.time()
else:
print("ERROR during LG handshake:")
else:
return False
Теперь проблема в том, что tvkey — это не то же самое, что wskey + key, но я думаю, что это неправильно.
подключить к телевизору tvkey = ключи ?!
чтобы определить проблему, я думаю, что это просто строка с
$pectedResonse = base64_encode (pack (‘H *’, sha1 ($ this-> ws_key. ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’)));
я не знаю этого в питоне …
Других решений пока нет …