PHP-Code:
<?php
class webOSTV
{
private $host, $port, $ws_key, $path, $lg_key, $sock, $connected=false, $handshaked=false;
function webOSTV($host, $port=3000, $lgKey="NOKEY", $path="/")
{
$this->host = $host;
$this->port = $port;
$this->lg_key = $lgKey;
$this->path = $path;
$this->ws_key = $key = base64_encode(generateRandomString(16, false, true));
if ($this->lg_key=="NOKEY") unset($this->lg_key);
}
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;
}
function generateRandomString($length = 10, $addSpaces = true, $addNumbers = true)
{
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"§$%&/()=[]{}';
$useChars = array();
// select some random chars:
for($i = 0; $i < $length; $i++)
{
$useChars[] = $characters[mt_rand(0, strlen($characters)-1)];
}
// add spaces and numbers:
if($addSpaces === true)
{
array_push($useChars, ' ', ' ', ' ', ' ', ' ', ' ');
}
if($addNumbers === true)
{
array_push($useChars, rand(0,9), rand(0,9), rand(0,9));
}
shuffle($useChars);
$randomString = trim(implode('', $useChars));
$randomString = substr($randomString, 0, $length);
return $randomString;
}
function json_array($str)
{
$result = json_decode(json_string($str),true);
return $result;
}
function json_string($str)
{
$from = strpos($str,"{");
$to = strripos($str,"}");
$len = $to-$from+1;
$result = substr($str,$from,$len);
return $result;
}
?>
und hier das passende Script dafür: