lgtv

    0.2.4 • Public • Published

    LGTV

    Installation

    npm install lgtv and set up the TV per below.

    Prerequisites

    First, the device (eg your computer) must be on the same network as the TV. Second, you should enable the TV to broadcast itself as lgsmarttv.lan in the local network. This setting is under Network/LG Connect Apps. This is necessary in order for this module to find the TV on the network and allow apps to connect. You also need to be on the same network as the TV.

    Quick start

    The first time you run it against the TV, you need to give the program access to the TV by answering yes to the prompt on the TV. From then on, the received client key is used so you don't have to perform this step again.

    Then, follow some of the examples to begin with, eg examples/show-float.js to show a float pop up on the screen:

    lgtv = require("lgtv");
     
    var tv_ip_address = "192.168.1.214";
    lgtv.connect(tv_ip_address, function(err, response){
      if (!err) {
        lgtv.show_float("It works!", function(err, response){
          if (!err) {
            lgtv.disconnect();
          }
        }); // show float
      }
    }); // connect

    Now that you can do this, we also can change input source to eg TV/HDMI/whatever, list and open apps, open browser, open Youtube app, change channel/volume, turn off the TV etc. Basically the only thing that doesn't work right now is a) turning on the TV, which doesn't seem possible this way, and b) opening Youtube at an URL (coming soon).

    Using a hostname or IP-address of the TV

    The above uses a default hostname, lgsmarttv.lan. Your TV may not follow that, or you may have more than one TV. Then you can specify the hostname like below. The hostname can be eg kitchen-tv.lan, 192.168.1.214 or similar.

    lgtv = require("lgtv");
     
    lgtv.connect("192.168.1.214", function(err, response){
      if (!err) {
        lgtv.show_float("It works!", function(err, response){
          if (!err) {
            lgtv.disconnect();
          }
        }); // show float
      }
    }); // connect

    Auto-detecting the TV on the network

    If you don't know the IP of the TV, or the hostname, you can scan for it using the discover_ip() function like below. Beware that this takes 3-4 seconds for the round-trip times (the TV is slow to respond to the SSDP discover probe).

    lgtv = require("lgtv");
     
    var retry_timeout = 10; // seconds
    lgtv.discover_ip(retry_timeout, function(err, ipaddr) {
      if (err) {
        console.log("Failed to find TV IP address on the LAN. Verify that TV is on, and that you are on the same LAN/Wifi.");
      } else {
        console.log("TV ip addr is: " + ipaddr);
      }
    });

    If you want to autodiscover each time, this would work,

    lgtv = require("lgtv");
     
    var retry_timeout = 10; // seconds
    lgtv.discover_ip(retry_timeout, function(err, ipaddr) {
      if (err) {
        console.log("Failed to find TV IP address on the LAN. Verify that TV is on, and that you are on the same LAN/Wifi.");
     
      } else {
        lgtv.connect(ipaddr, function(err, response){
          if (!err) {
            lgtv.show_float("Found you!", function(err, response){
              if (!err) {
                lgtv.disconnect();
              }
            }); // show float
          }
        }); // connect
      }
    });

    Introduction

    This module is targeting the LG Smart TVs running WebOS, ie later 2014 or 2015 models. Previous models used another OS and other protocols and won't work with this.

    • Controlling the TV means
      • (finding the TV on your local network)
      • establishing a connection, ie successful handshake
      • controlling input source, volume, etc

    There is some useful information out there already:

    Motivation

    There is an LG remote control app for Android, but it is horribly slow. Also, it is very generic and mirrors the physical remote control. With this module I can chain a set of commands such as change input to HDMI_1 and set volume 10 and make them happen programmatically instead of finding the right buttons in the app. I also combine this with a corresponding module for controlling a Kodi media player.

    Communication overview

    I recently bought a new TV, a LG 60LB870V, which is a 2014 TV running WebOS 1.x. The same day I got the TV, I ran nmap on the TV and Wireshark on the network the TV was connected to, with the following results (full results at the bottom).

    Port Scanning host results
         Open TCP Port:     1061        
         Open TCP Port:     1424        
         Open TCP Port:     1900        ssdp
         Open TCP Port:     1970        
         Open TCP Port:     3000        ws
         Open TCP Port:     3001        wss
         Open TCP Port:     9955
         Open TCP Port:     9998        
         Open TCP Port:     18181       
         Open TCP Port:     36866
    

    Through Wireshark, I saw the TV sending UDP:

    • SSDP (simple service discovery protocol) to 239.255.255.250:1900, presenting several SSDP endpoints
    • 192.168.1.255:9956 and 224.0.0.113:9956. Port 9956 and the contents show this is alljoyn-traffic, something I haven't encountered before but is a service discovery protocol of some kind according to Wikipedia. The addresses are multicast/broadcast.

    In the TV menus I had also enabled zeroconf meaning I can now address the TV by an address valid in the local network, by default lgsmarttv.lan which is found by mDNS. This setting is, IIRC, under Network/LG Connect Apps.

    The TV IP address can otherwise be found using SSDP; send this:

    'M-SEARCH * HTTP/1.1\r\n'
    'HOST: 239.255.255.250:1900\r\n'
    'MX: 30\r\n'
    'MAN: "ssdp:discover\r\n"'
    'ST: urn:lge-com:service:webos-second-screen:1\r\n\r\n'
    

    to udp://239.255.255.250:1900, then the TV will respond. This may be an alternative schema: urn:schemas-upnp-org:device:MediaRenderer:1.

    Pairing and communication

    Most of the communication is over websockets on port 3000, or 3001.

    The application must pair with the TV in order to be allowed to control it. The pairing handshake used here is a hardcoded handshake retrieved from another LG remote control application, which in turn seems to have retrieved it from the official LG remote control app. No fields can be changed or the handshake will fail, and only basic commands are allowed. The handshake contains a base-64 signature, which if "debased" starts {"algorithm":"RSA-SHA256","keyId":"test-signing-cert","signatureVersion":1}. This may just be a hash of the signature, perhaps in JSON format, but I haven't persued this further.

    If the signing information is not included, or something is changed - thus invalidating the signing - the handshake will still succeed, but some commands are not permitted (such as getting information about the TV software).

    After the handshake, the rest of the communication stays over the same websocket socket. Data and commands are sent in cleartext JSON format, eg

    {"type":"response","id":"status_0","payload":{"scenario":"mastervolume_tv_speaker","active":false,"action":"requested","volume":0,"returnValue":true,"subscribed":true,"mute":false}}
    

    The type is either (at least, there may be more),

    • request - a single request, eg get volume
    • response - response to a request, or subscription event
    • subscribe - subscribe to a topic ie get notifications when something happens, eg channel is changed
    • unsubscribe - unsubscribe a subscribed topic

    The id is a concatenation of the command and a message counter, like so:

    Request:
    {"type":"request","id":"status_3", ...}
    
    Response:
    {"type":"response","id":"status_3", ...}
    

    This is used so that a request can be matched with a response.


    Complete nmap report

    Command: sudo nmap -sV -p 1-65535 192.168.1.86

    Result:

    Starting Nmap 6.40-2 ( http://nmap.org ) at 2014-12-30 14:13 CET
    Nmap scan report for 192.168.1.86
    Host is up (0.028s latency).
    Not shown: 65525 closed ports
    PORT      STATE SERVICE     VERSION
    1126/tcp  open  tcpwrapped
    1261/tcp  open  tcpwrapped
    1843/tcp  open  tcpwrapped
    1900/tcp  open  upnp?
    3000/tcp  open  ppp?
    3001/tcp  open  ssl/nessus?
    9955/tcp  open  unknown
    9998/tcp  open  distinct32?
    18181/tcp open  opsec-cvp?
    36866/tcp open  unknown
    6 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at http://www.insecure.org/cgi-bin/servicefp-submit.cgi :
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port1900-TCP:V=6.40-2%I=7%D=12/30%Time=54A2A53E%P=x86_64-apple-darwin10
    SF:.8.0%r(GetRequest,52,"HTTP/1\.1\x20404\x20Not\x20Found\r\nDate:\x20Tue,
    SF:\x2030\x20Dec\x202014\x2013:13:48\x20GMT\r\nConnection:\x20close\r\n\r\
    SF:n")%r(HTTPOptions,52,"HTTP/1\.1\x20404\x20Not\x20Found\r\nDate:\x20Tue,
    SF:\x2030\x20Dec\x202014\x2013:13:53\x20GMT\r\nConnection:\x20close\r\n\r\
    SF:n")%r(FourOhFourRequest,52,"HTTP/1\.1\x20404\x20Not\x20Found\r\nDate:\x
    SF:20Tue,\x2030\x20Dec\x202014\x2013:13:53\x20GMT\r\nConnection:\x20close\
    SF:r\n\r\n");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port3000-TCP:V=6.40-2%I=7%D=12/30%Time=54A2A543%P=x86_64-apple-darwin10
    SF:.8.0%r(GetRequest,58,"HTTP/1\.1\x20200\x20OK\r\nDate:\x20Tue,\x2030\x20
    SF:Dec\x202014\x2013:13:53\x20GMT\r\nConnection:\x20close\r\n\r\nHello\x20
    SF:world\r\n")%r(HTTPOptions,58,"HTTP/1\.1\x20200\x20OK\r\nDate:\x20Tue,\x
    SF:2030\x20Dec\x202014\x2013:13:53\x20GMT\r\nConnection:\x20close\r\n\r\nH
    SF:ello\x20world\r\n");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port3001-TCP:V=6.40-2%T=SSL%I=7%D=12/30%Time=54A2A561%P=x86_64-apple-da
    SF:rwin10.8.0%r(GetRequest,58,"HTTP/1\.1\x20200\x20OK\r\nDate:\x20Tue,\x20
    SF:30\x20Dec\x202014\x2013:14:23\x20GMT\r\nConnection:\x20close\r\n\r\nHel
    SF:lo\x20world\r\n")%r(HTTPOptions,58,"HTTP/1\.1\x20200\x20OK\r\nDate:\x20
    SF:Tue,\x2030\x20Dec\x202014\x2013:14:23\x20GMT\r\nConnection:\x20close\r\
    SF:n\r\nHello\x20world\r\n");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port9955-TCP:V=6.40-2%I=7%D=12/30%Time=54A2A573%P=x86_64-apple-darwin10
    SF:.8.0%r(Kerberos,F,"ERROR\x20Unknown\r\n");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port9998-TCP:V=6.40-2%I=7%D=12/30%Time=54A2A543%P=x86_64-apple-darwin10
    SF:.8.0%r(GetRequest,5C8,"HTTP/1\.1\x20200\x20OK\r\nConnection:\x20close\r
    SF:\nContent-Length:\x201395\r\nContent-Type:\x20text/html\r\n\r\n<!DOCTYP
    SF:E\x20html>\n<html><head>\n<script\x20type=\"text/javascript\">\nfunctio
    SF:n\x20createPageList\(\)\x20{\n\x20\x20\x20\x20var\x20xhr\x20=\x20new\x2
    SF:0XMLHttpRequest;\n\x20\x20\x20\x20xhr\.open\(\"GET\",\x20\"/pagelist\.j
    SF:son\"\);\n\x20\x20\x20\x20xhr\.onload\x20=\x20function\(e\)\x20{\n\x20\
    SF:x20\x20\x20\x20\x20\x20\x20if\x20\(xhr\.status\x20==\x20200\)\x20{\n\x2
    SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20pages\x20=\x20JSON\
    SF:.parse\(xhr\.responseText\);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
    SF:x20\x20if\x20\(pages\.length\)\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
    SF:0\x20\x20\x20\x20\x20\x20document\.getElementById\(\"noPageNotice\"\)\.
    SF:style\.display\x20=\x20\"none\";\n\n\x20\x20\x20\x20\x20\x20\x20\x20\x2
    SF:0\x20\x20\x20var\x20pageList\x20=\x20document\.createElement\(\"ol\"\);
    SF:\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20for\x20\(var\x20i\x20
    SF:in\x20pages\)\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
    SF:0\x20\x20\x20var\x20link\x20=\x20document\.createElement\(\"a\"\);\n\x2
    SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20tit
    SF:le\x20=\x20pages\[i\]\.title\x20\?\x20pages\[i\]\.title\x20:\x20\(\"Pag
    SF:e\x20\"\x20\+\x20\(Number\(pages\[i\]\.id\)\)\);\n\x20\x20\x20\x20\x20\
    SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20url\x20=\x20pages\[i\
    SF:]\.url;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
    SF:20link\.appendChild\(document\.createTextNode\(title\x20\+\x20\(url\x20
    SF:\?\x20\(\"\x20\[\"\x20\+\x20url\x20\+\x20\"\]\"\)\x20:\x20\"\"\x20\)\)\
    SF:);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20lin
    SF:k\.setAttribute\(\"hre")%r(HTTPOptions,5C8,"HTTP/1\.1\x20200\x20OK\r\nC
    SF:onnection:\x20close\r\nContent-Length:\x201395\r\nContent-Type:\x20text
    SF:/html\r\n\r\n<!DOCTYPE\x20html>\n<html><head>\n<script\x20type=\"text/j
    SF:avascript\">\nfunction\x20createPageList\(\)\x20{\n\x20\x20\x20\x20var\
    SF:x20xhr\x20=\x20new\x20XMLHttpRequest;\n\x20\x20\x20\x20xhr\.open\(\"GET
    SF:\",\x20\"/pagelist\.json\"\);\n\x20\x20\x20\x20xhr\.onload\x20=\x20func
    SF:tion\(e\)\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20if\x20\(xhr\.status\x20
    SF:==\x20200\)\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x
    SF:20pages\x20=\x20JSON\.parse\(xhr\.responseText\);\n\x20\x20\x20\x20\x20
    SF:\x20\x20\x20\x20\x20\x20\x20if\x20\(pages\.length\)\n\x20\x20\x20\x20\x
    SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20document\.getElementById\
    SF:(\"noPageNotice\"\)\.style\.display\x20=\x20\"none\";\n\n\x20\x20\x20\x
    SF:20\x20\x20\x20\x20\x20\x20\x20\x20var\x20pageList\x20=\x20document\.cre
    SF:ateElement\(\"ol\"\);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
    SF:for\x20\(var\x20i\x20in\x20pages\)\x20{\n\x20\x20\x20\x20\x20\x20\x20\x
    SF:20\x20\x20\x20\x20\x20\x20\x20\x20var\x20link\x20=\x20document\.createE
    SF:lement\(\"a\"\);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
    SF:x20\x20\x20var\x20title\x20=\x20pages\[i\]\.title\x20\?\x20pages\[i\]\.
    SF:title\x20:\x20\(\"Page\x20\"\x20\+\x20\(Number\(pages\[i\]\.id\)\)\);\n
    SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20
    SF:url\x20=\x20pages\[i\]\.url;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
    SF:x20\x20\x20\x20\x20\x20link\.appendChild\(document\.createTextNode\(tit
    SF:le\x20\+\x20\(url\x20\?\x20\(\"\x20\[\"\x20\+\x20url\x20\+\x20\"\]\"\)\
    SF:x20:\x20\"\"\x20\)\)\);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
    SF:20\x20\x20\x20\x20link\.setAttribute\(\"hre");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port36866-TCP:V=6.40-2%I=7%D=12/30%Time=54A2A543%P=x86_64-apple-darwin1
    SF:0.8.0%r(GetRequest,198,"HTTP/1\.1\x20200\x20OK\r\nContent-Type:\x20text
    SF:/xml\r\nDate:\x20Tue,\x2030\x20Dec\x202014\x2013:13:53\x20GMT\r\nConnec
    SF:tion:\x20close\r\n\r\n<\?xml\x20version=\"1\.0\"\x20encoding=\"UTF-8\"\
    SF:?><service\x20xmlns=\"urn:dial-multiscreen-org:schemas:dial\"><name></n
    SF:ame><options\x20allowStop=\"true\"/><state>running</state><link\x20rel=
    SF:\"run\"\x20href=\"run\"/><additionalData\x20xmlns=\"http://www\.youtube
    SF:\.com/dial\"><screenId>lf85fem4srqg84i7vis9ppj0ie</screenId></additiona
    SF:lData></service>")%r(FourOhFourRequest,52,"HTTP/1\.1\x20404\x20Not\x20F
    SF:ound\r\nDate:\x20Tue,\x2030\x20Dec\x202014\x2013:13:59\x20GMT\r\nConnec
    SF:tion:\x20close\r\n\r\n");
    MAC Address: <redacted> (Unknown)
    
    Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
    Nmap done: 1 IP address (1 host up) scanned in 209.82 seconds
    

    Update, with the latest firmware as of today, the 1.4.0-2507(afro-ashley), the nmap looks like below. Also, I don't see it advertising itself over SSDP like before, nor answer to the ssdp query as it did before.

    -[691]> sudo nmap -sV -p 1-65535 192.168.1.86
    Password:
    
    Starting Nmap 6.40-2 ( http://nmap.org ) at 2015-10-29 21:37 CET
    Nmap scan report for 192.168.1.86
    Host is up (0.0076s latency).
    Not shown: 65525 closed ports
    PORT      STATE SERVICE     VERSION
    1701/tcp  open  tcpwrapped
    1742/tcp  open  tcpwrapped
    1881/tcp  open  tcpwrapped
    1900/tcp  open  upnp?
    3000/tcp  open  ppp?
    3001/tcp  open  ssl/nessus?
    9955/tcp  open  unknown
    9998/tcp  open  distinct32?
    18181/tcp open  opsec-cvp?
    36866/tcp open  unknown
    6 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at http://www.insecure.org/cgi-bin/servicefp-submit.cgi :
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port1900-TCP:V=6.40-2%I=7%D=10/29%Time=563283C6%P=x86_64-apple-darwin10
    SF:.8.0%r(GetRequest,52,"HTTP/1\.1\x20404\x20Not\x20Found\r\nDate:\x20Thu,
    SF:\x2029\x20Oct\x202015\x2020:38:26\x20GMT\r\nConnection:\x20close\r\n\r\
    SF:n")%r(HTTPOptions,52,"HTTP/1\.1\x20404\x20Not\x20Found\r\nDate:\x20Thu,
    SF:\x2029\x20Oct\x202015\x2020:38:31\x20GMT\r\nConnection:\x20close\r\n\r\
    SF:n")%r(FourOhFourRequest,52,"HTTP/1\.1\x20404\x20Not\x20Found\r\nDate:\x
    SF:20Thu,\x2029\x20Oct\x202015\x2020:38:31\x20GMT\r\nConnection:\x20close\
    SF:r\n\r\n");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port3000-TCP:V=6.40-2%I=7%D=10/29%Time=563283CB%P=x86_64-apple-darwin10
    SF:.8.0%r(GetRequest,58,"HTTP/1\.1\x20200\x20OK\r\nDate:\x20Thu,\x2029\x20
    SF:Oct\x202015\x2020:38:31\x20GMT\r\nConnection:\x20close\r\n\r\nHello\x20
    SF:world\r\n")%r(HTTPOptions,58,"HTTP/1\.1\x20200\x20OK\r\nDate:\x20Thu,\x
    SF:2029\x20Oct\x202015\x2020:38:31\x20GMT\r\nConnection:\x20close\r\n\r\nH
    SF:ello\x20world\r\n");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port3001-TCP:V=6.40-2%T=SSL%I=7%D=10/29%Time=563283E8%P=x86_64-apple-da
    SF:rwin10.8.0%r(GetRequest,58,"HTTP/1\.1\x20200\x20OK\r\nDate:\x20Thu,\x20
    SF:29\x20Oct\x202015\x2020:39:00\x20GMT\r\nConnection:\x20close\r\n\r\nHel
    SF:lo\x20world\r\n")%r(HTTPOptions,58,"HTTP/1\.1\x20200\x20OK\r\nDate:\x20
    SF:Thu,\x2029\x20Oct\x202015\x2020:39:00\x20GMT\r\nConnection:\x20close\r\
    SF:n\r\nHello\x20world\r\n");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port9955-TCP:V=6.40-2%I=7%D=10/29%Time=563283D5%P=x86_64-apple-darwin10
    SF:.8.0%r(Kerberos,F,"ERROR\x20Unknown\r\n");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port9998-TCP:V=6.40-2%I=7%D=10/29%Time=563283CB%P=x86_64-apple-darwin10
    SF:.8.0%r(GetRequest,5C8,"HTTP/1\.1\x20200\x20OK\r\nConnection:\x20close\r
    SF:\nContent-Length:\x201395\r\nContent-Type:\x20text/html\r\n\r\n<!DOCTYP
    SF:E\x20html>\n<html><head>\n<script\x20type=\"text/javascript\">\nfunctio
    SF:n\x20createPageList\(\)\x20{\n\x20\x20\x20\x20var\x20xhr\x20=\x20new\x2
    SF:0XMLHttpRequest;\n\x20\x20\x20\x20xhr\.open\(\"GET\",\x20\"/pagelist\.j
    SF:son\"\);\n\x20\x20\x20\x20xhr\.onload\x20=\x20function\(e\)\x20{\n\x20\
    SF:x20\x20\x20\x20\x20\x20\x20if\x20\(xhr\.status\x20==\x20200\)\x20{\n\x2
    SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20pages\x20=\x20JSON\
    SF:.parse\(xhr\.responseText\);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
    SF:x20\x20if\x20\(pages\.length\)\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
    SF:0\x20\x20\x20\x20\x20\x20document\.getElementById\(\"noPageNotice\"\)\.
    SF:style\.display\x20=\x20\"none\";\n\n\x20\x20\x20\x20\x20\x20\x20\x20\x2
    SF:0\x20\x20\x20var\x20pageList\x20=\x20document\.createElement\(\"ol\"\);
    SF:\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20for\x20\(var\x20i\x20
    SF:in\x20pages\)\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
    SF:0\x20\x20\x20var\x20link\x20=\x20document\.createElement\(\"a\"\);\n\x2
    SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20tit
    SF:le\x20=\x20pages\[i\]\.title\x20\?\x20pages\[i\]\.title\x20:\x20\(\"Pag
    SF:e\x20\"\x20\+\x20\(Number\(pages\[i\]\.id\)\)\);\n\x20\x20\x20\x20\x20\
    SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20url\x20=\x20pages\[i\
    SF:]\.url;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
    SF:20link\.appendChild\(document\.createTextNode\(title\x20\+\x20\(url\x20
    SF:\?\x20\(\"\x20\[\"\x20\+\x20url\x20\+\x20\"\]\"\)\x20:\x20\"\"\x20\)\)\
    SF:);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20lin
    SF:k\.setAttribute\(\"hre")%r(HTTPOptions,5C8,"HTTP/1\.1\x20200\x20OK\r\nC
    SF:onnection:\x20close\r\nContent-Length:\x201395\r\nContent-Type:\x20text
    SF:/html\r\n\r\n<!DOCTYPE\x20html>\n<html><head>\n<script\x20type=\"text/j
    SF:avascript\">\nfunction\x20createPageList\(\)\x20{\n\x20\x20\x20\x20var\
    SF:x20xhr\x20=\x20new\x20XMLHttpRequest;\n\x20\x20\x20\x20xhr\.open\(\"GET
    SF:\",\x20\"/pagelist\.json\"\);\n\x20\x20\x20\x20xhr\.onload\x20=\x20func
    SF:tion\(e\)\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20if\x20\(xhr\.status\x20
    SF:==\x20200\)\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x
    SF:20pages\x20=\x20JSON\.parse\(xhr\.responseText\);\n\x20\x20\x20\x20\x20
    SF:\x20\x20\x20\x20\x20\x20\x20if\x20\(pages\.length\)\n\x20\x20\x20\x20\x
    SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20document\.getElementById\
    SF:(\"noPageNotice\"\)\.style\.display\x20=\x20\"none\";\n\n\x20\x20\x20\x
    SF:20\x20\x20\x20\x20\x20\x20\x20\x20var\x20pageList\x20=\x20document\.cre
    SF:ateElement\(\"ol\"\);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
    SF:for\x20\(var\x20i\x20in\x20pages\)\x20{\n\x20\x20\x20\x20\x20\x20\x20\x
    SF:20\x20\x20\x20\x20\x20\x20\x20\x20var\x20link\x20=\x20document\.createE
    SF:lement\(\"a\"\);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
    SF:x20\x20\x20var\x20title\x20=\x20pages\[i\]\.title\x20\?\x20pages\[i\]\.
    SF:title\x20:\x20\(\"Page\x20\"\x20\+\x20\(Number\(pages\[i\]\.id\)\)\);\n
    SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20var\x20
    SF:url\x20=\x20pages\[i\]\.url;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
    SF:x20\x20\x20\x20\x20\x20link\.appendChild\(document\.createTextNode\(tit
    SF:le\x20\+\x20\(url\x20\?\x20\(\"\x20\[\"\x20\+\x20url\x20\+\x20\"\]\"\)\
    SF:x20:\x20\"\"\x20\)\)\);\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
    SF:20\x20\x20\x20\x20link\.setAttribute\(\"hre");
    ==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
    SF-Port36866-TCP:V=6.40-2%I=7%D=10/29%Time=563283CB%P=x86_64-apple-darwin1
    SF:0.8.0%r(GetRequest,145,"HTTP/1\.1\x20200\x20OK\r\nContent-Type:\x20text
    SF:/xml\r\nDate:\x20Thu,\x2029\x20Oct\x202015\x2020:38:31\x20GMT\r\nConnec
    SF:tion:\x20close\r\n\r\n<\?xml\x20version=\"1\.0\"\x20encoding=\"UTF-8\"\
    SF:?><service\x20xmlns=\"urn:dial-multiscreen-org:schemas:dial\"><name></n
    SF:ame><options\x20allowStop=\"true\"/><state>running</state><link\x20rel=
    SF:\"run\"\x20href=\"run\"/><additionalData></additionalData></service>")%
    SF:r(FourOhFourRequest,52,"HTTP/1\.1\x20404\x20Not\x20Found\r\nDate:\x20Th
    SF:u,\x2029\x20Oct\x202015\x2020:38:36\x20GMT\r\nConnection:\x20close\r\n\
    SF:r\n");
    MAC Address: <redacted> (Unknown)
    
    Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
    Nmap done: 1 IP address (1 host up) scanned in 180.22 seconds
    

    Install

    npm i lgtv

    DownloadsWeekly Downloads

    15

    Version

    0.2.4

    License

    MIT

    Last publish

    Collaborators

    • msloth