diff --git a/LGTV/__init__.py b/LGTV/__init__.py index fa4a847..7ec93a2 100644 --- a/LGTV/__init__.py +++ b/LGTV/__init__.py @@ -16,6 +16,7 @@ from remote import LGTVRemote from auth import LGTVAuth from cursor import LGTVCursor +from instart import LGTVInStart config_paths = [ @@ -187,7 +188,7 @@ def main(): try: kwargs = parseargs(args.command, args.args) except Exception: - if args.command not in {"sendButton"}: + if args.command not in {"sendButton", "inStart", "ezAdjust"}: parser.print_help() sys.exit(1) @@ -209,6 +210,13 @@ def main(): cursor.execute(args.args) return + if args.command in ("inStart", "ezAdjust"): + irKey = "ezAdjust" if args.command == "ezAdjust" else "inStart" + pin = args.args[0] if args.args else "0413" + ws = LGTVInStart(name, **config[name], ssl=args.ssl, irKey=irKey) + ws.unlock(pin) + return + max_retries = 3 for attempt in range(max_retries): try: diff --git a/LGTV/instart.py b/LGTV/instart.py new file mode 100644 index 0000000..312a815 --- /dev/null +++ b/LGTV/instart.py @@ -0,0 +1,61 @@ +from time import sleep +import json +import logging + +from ws4py.client.threadedclient import WebSocketClient + +from remote import LGTVRemote + + +class LGTVInStart(WebSocketClient): + # The factory menus (In-Start, EZ Adjust) live in com.webos.app.factorywin + # but stay invisible unless launched with the executeFactory trigger and + # unlocked by sending the service PIN over the pointer input socket. + + def __finalize(self, response): + payload = response.get("payload", {}) + if payload.get("socketPath"): + super(LGTVInStart, self).__init__(payload["socketPath"], exclude_headers=["Origin"]) + self.__ready = True + else: + print(json.dumps(response)) + self.remote.close() + + def __launchResult(self, response): + if not response.get("payload", {}).get("returnValue"): + print(json.dumps(response)) + + def __init__(self, name, ip=None, mac=None, key=None, hostname=None, ssl=False, irKey="inStart"): + self.__ready = False + self.__irKey = irKey + + self.remote = LGTVRemote(name, ip, mac, key, hostname, ssl) + self.remote.connect() + self.remote.execute("openAppWithPayload", { + "payload": { + "id": "com.webos.app.factorywin", + "params": {"id": "executeFactory", "irKey": irKey} + }, + "callback": self.__launchResult + }) + self.remote.execute("getCursorSocket", {"callback": self.__finalize}) + self.remote.run_forever() + + def unlock(self, pin="0413"): + if not self.__ready: + logging.error("Could not open the factory menu") + return + # factorywin needs a moment before it listens for the PIN + sleep(2) + self.connect() + for digit in str(pin): + self.send("type:button\nname:%s\n\n" % digit) + sleep(0.3) + sleep(0.5) + self.close() + print(json.dumps({ + "factoryMenu": { + "irKey": self.__irKey, + "pin": str(pin), + } + })) diff --git a/README.md b/README.md index 1bb09e1..2766e1e 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,8 @@ All devices with firmware major version 33, product name "webOSTV 25" lgtv --name MyTV --ssl inputMediaPlay lgtv --name MyTV --ssl inputMediaRewind lgtv --name MyTV --ssl inputMediaStop + lgtv --name MyTV --ssl inStart [pin] + lgtv --name MyTV --ssl ezAdjust [pin] lgtv --name MyTV --ssl listApps lgtv --name MyTV --ssl listLaunchPoints lgtv --name MyTV --ssl listChannels