def __init__(self): webos_store = {'auth': {}} try: webos_store = pickle.load(open("webos.auth", "rb")) except: print("Cant load webos auth, registering new one.") pass if not 'host' in webos_store: print("Searching tv...") webos_client = WebOSClient.discover()[0] else: print("Connecting to tv at {}".format(webos_store['host'])) webos_client = WebOSClient(webos_store['host']) webos_client.connect() for status in webos_client.register(webos_store['auth']): if status == WebOSClient.PROMPTED: print("Please accept the connect on the TV!") elif status == WebOSClient.REGISTERED: print("Webos connection successful!") webos_store['host'] = webos_client.host pickle.dump(webos_store, open("webos.auth", "wb")) self.webos_system = SystemControl(webos_client) self.webos_media = MediaControl(webos_client) self.webos_inp = InputControl(webos_client) self.webos_inp.connect_input() self.notify("Skipper connected")
def __init__(self): """Client key will be displayed on the TV when you accept the connection for the first time. Store the dict value as an env variable and use it as below. Using TV's ip makes the initial response much quicker but it looks for the TVs in ip range if an ip is not found.""" store = {'client_key': client_key} try: self.client = WebOSClient(ip_address) self.client.connect() except gaierror: sys.stdout.write( f"\rThe TV's IP has either changed or unreachable. Scanning your IP range.." ) self.client = WebOSClient.discover()[0] self.client.connect() except (TimeoutError, BrokenPipeError): sys.stdout.write( '\rOperation timed out. The TV might be turned off.') for status in self.client.register(store): if status == WebOSClient.REGISTERED and not TV._init_status: sys.stdout.write('\rConnected to the TV.') elif status == WebOSClient.PROMPTED: sys.stdout.write( '\rPlease accept the connection request on your TV.') # sys.stdout.write(f'\r{store}') # get the client key dict during the first run and store as in line #18 self.system = SystemControl(self.client) self.system.notify("Jarvis is controlling the TV now." ) if not TV._init_status else None self.media = MediaControl(self.client) self.app = ApplicationControl(self.client) self.source_control = SourceControl(self.client) TV._init_status = True
def test_volume_down(self): client = WebOSClient("ws://a") media = MediaControl(client) media.volume_down() self.assert_sent_message_without_id({ "type": "request", "uri": "ssap://audio/volumeDown" })
def test_unmute(self): client = FakeClient() media = MediaControl(client) media.mute(False, block=False) client.assert_sent_message_without_id({ "type": "request", "uri": "ssap://audio/setMute", "payload": {"mute": False} })
def test_set_volume(self): client = FakeClient() media = MediaControl(client) media.set_volume(30, block=False) client.assert_sent_message_without_id({ "type": "request", "uri": "ssap://audio/setVolume", "payload": {"volume": 30} })
def test_unmute(self): client = WebOSClient("ws://a") media = MediaControl(client) media.mute(False) self.assert_sent_message_without_id({ "type": "request", "uri": "ssap://audio/setMute", "payload": { "mute": False } })
def set_volume(self): client = WebOSClient("ws://a") media = MediaControl(client) media.set_volume(30) self.assert_sent_message_without_id({ "type": "request", "uri": "ssap://audio/setVolume", "payload": { "volume": 30 } })
class WebOsConnection: def __init__(self):#TODO pass args with open('tv_conf.json', 'r') as fileConf:#TODO do not read from file dataConf = fileConf.read() self.store = json.loads(dataConf) self.clientIp = self.store['ip_address'] self.macAddress = self.store['mac_address'] self.tvName = self.store['name'] self.client = WebOSClient(self.clientIp) self.isConnected = False self.systemControl = None self.mediaControl = None def get_client(self): if self.isConnected is False: # Wake on lan send_magic_packet(self.macAddress) #connect to the host self.client.connect() for status in self.client.register(self.store): if status == WebOSClient.PROMPTED: print("Please accept the connect on the TV!") elif status == WebOSClient.REGISTERED: print("Registration successful!") self.isConnected = True return self.client def disconnect_client(self): self.isConnected = False def switchOn(self): if self.systemControl is None: self.systemControl = SystemControl(self.get_client()) #self.systemControl.power_on() #TODO non esiste power_on() def switchOff(self): if self.systemControl is None: self.systemControl = SystemControl(self.get_client()) self.systemControl.power_off() self.disconnect_client() def setVolume(self, volume): if self.mediaControl is None: self.mediaControl = MediaControl(self.get_client()) self.mediaControl.set_volume(volume)
def control(self): if self._control is None: self._control = MediaControl( self.adapter.create(str(Path().home() / '.lgtv.yaml'), 'living_room')) return self._control
def connect_and_register_to_tv(self): # self.client = WebOSClient.discover()[0] self.client = WebOSClient(self.tv_context['local_address']) self.client.connect() register_status = self.client.register(store=self.tv_context) for status in register_status: if status == WebOSClient.PROMPTED: print("Please accept the connect on the TV!") elif status == WebOSClient.REGISTERED: print("Registration successful!") self.input_control = InputControl(self.client) self.input_control.connect_input() self.system_control = SystemControl(self.client) self.media_control = MediaControl(self.client)
def get_apis(client): media = MediaControl(client) system = SystemControl(client) app = ApplicationControl(client) inp = InputControl(client) return [ ServerAPI( "mute", "Mute the TV", [ArgParameter("state", "True to mute, False to unmute", bool)], media.mute), ServerAPI("volume_up", "Increase Volume", [], media.volume_up), ServerAPI("volume_down", "Decrease Volume", [], media.volume_down), ServerAPI("set_volum", "Set Volume", [ArgParameter("level", "Volume Level", int)], media.set_volume), ServerAPI("play", "Play", [], media.play), ServerAPI("pause", "Pause", [], media.pause), ServerAPI("rewind", "Rewind", [], media.rewind), ServerAPI("fast_forward", "Fast Forward", [], media.fast_forward), ServerAPI("power_off", "Power off TV", [], system.power_off), ServerAPI("notify", "Send notification", [ArgParameter("text", "Text to notify", str)], system.notify), ServerAPI("type", "Send text input", [ArgParameter("text", "Text to insert", str)], inp.type), ServerAPI("delete", "Delete text", [ArgParameter("count", "No of characters to delete", int)], inp.delete), ServerAPI("enter", "Press Enter", [], inp.enter), ]
def __init__(self, client): self.client: WebOSClient = client self._system: SystemControl = SystemControl(client) self._media: MediaControl = MediaControl(client) self._app: ApplicationControl = ApplicationControl(client) self._inp: InputControl = InputControl(client)
def __init__(self): self.ws = WebOSClient(S.config.get('host')) self.ws.connect() assert (2 in self.ws.register(S.config)), "Not registered to TV yet" self.ac = ApplicationControl(self.ws) self.ic = InputControl(self.ws) self.mc = MediaControl(self.ws) self.sc = SystemControl(self.ws) self.srcc = SourceControl(self.ws) self.tv = TvControl(self.ws)
def init(args): if os.path.exists(os.path.expanduser(args.configFile)): with open(os.path.expanduser(args.configFile), 'r') as fp: S.config = json.load(fp) if 'host' in S.config: S.base = WebOSClient(S.config['host']) S.base.connect() assert(2 in S.base.register(S.config)), "Not registered yet" # S.app = ApplicationControl(S.base) # S.sys, S.tv = SystemControl(S.base), TvControl(S.base) # S.med, S.inp = MediaControl(S.base), InputControl(S.base) S.med = MediaControl(S.base)
def get_control(self, controltype): if controltype not in self.clients: if controltype == 'media': control = MediaControl(self.client) elif controltype == 'input': control = InputControl(self.client) control.connect_input() elif controltype == 'system': control = SystemControl(self.client) elif controltype == 'source': control = SourceControl(self.client) self.clients[controltype] = control return self.clients[controltype]
def __init__(self, configFile="~/.config/lgtv"): log = logging.getLogger(__name__) self.config = self.load_config(configFile) or {} self.ws = WebOSClient(self.config.get('host')) self.ws.connect() assert (2 in self.ws.register(self.config)), "Not registered to TV yet" self.ac = ApplicationControl(self.ws) self.ic = InputControl(self.ws) self.mc = MediaControl(self.ws) self.sc = SystemControl(self.ws) self.srcc = SourceControl(self.ws) self.tv = TvControl(self.ws) self.apps = {a["id"]: a for a in self.ac.list_apps()} # self.ac.subscribe_get_current(self.__on_app_changed) # self.mc.subscribe_get_volume(self.__on_volume_changed) self.current = self.volume = None
def conn_attempt(): global board, connection_status, tvClient, tvInputControl, tvMediaControl print('Attempting to connect') connection_status = 1 board.led.state = Led.PULSE_QUICK board.button._pressed_callback = None client_list = WebOSClient.discover() if len(client_list) != 1: play_wav('errorsound.wav') print('connection failed: more than 1 tv found') connection_status = 0 board.led.state = Led.OFF return tvClient = client_list[0] try: tvClient.connect() for status in tvClient.register(store): if status == WebOSClient.PROMPTED: play_wav('ding.wav') print('See TV prompt and press yes') elif status == WebOSClient.REGISTERED: play_wav('ding.wav') print('Successful connection') board.led.state = Led.ON connection_status = 2 tvInputControl = InputControl(tvClient) tvInputControl.connect_input() tvMediaControl = MediaControl(tvClient) except Exception: play_wav('errorsound.wav') print('connection failed: you probably pressed no on the prompt') connection_status = 0 board.led.state = Led.OFF return
def test_commands(self, command, uri): client = FakeClient() media = MediaControl(client) getattr(media, command)(block=False) client.assert_sent_message_without_id({"type": "request", "uri": uri})
def test_get_volume(self): client = FakeClient() res = dict(returnValue=True, volume=1, mute=True, scenario="") client.setup_response("ssap://audio/getVolume", res) media = MediaControl(client) assert media.get_volume(block=True)["volume"] == 1
class PyTVCursor: ''' Implements a smaller coordinate window of size self.width x self.height within the larger coordinate system. This window is shifted within the larger coordinate system such that the cursor drags the window along with it. The current x,y position of the window within the larger coodinate system is given by self.minx and self.miny The position of the cursor within the window is given by self._x and self._y The pos properties are used to pass the current external coordinate and retrieve the internal coordinate ''' def __init__(self): # X, Y, Height, width of window in world coordinates +y up self.world_window_height = 100.0 self.world_window_width = 120.0 self.world_window_x = -60.0 self.world_window_y = -50.0 # Height and width of window in tv coordinates self.tv_window_half_height = 50 self.tv_window_half_width = 85 # Current cursor coordinate in tv coordinates, starts in center of screen +y down self.tv_cursor_x = 0 self.tv_cursor_y = 0 self.last_click_ms = 0 self.paused = False self.verbose = True self.client = None self.input_control = None self.system_control = None self.media_control = None self.tv_context = { 'client_key': 'c8bfd5128b861d24a0ffbf6644eb6f18', 'local_address': '10.0.0.214' # client.peer_address[0] } self.connect_and_register_to_tv() def update_world_coordinate(self, pos): if pos is None or len(pos) != 2: return # shift window in x direction self.world_window_x = min(pos[0], self.world_window_x) if pos[0] > self.world_window_x + self.world_window_width: self.world_window_x = pos[0] - self.world_window_width world_window_x_center = self.world_window_x + 0.5 * self.world_window_width x_unit_vector = (pos[0] - world_window_x_center) / (self.world_window_width * 0.5) # shift window in y direction and calculate vector self.world_window_y = min(pos[1], self.world_window_y) if pos[1] > self.world_window_y + self.world_window_height: self.world_window_y = pos[1] - self.world_window_height world_window_y_center = self.world_window_y + 0.5 * self.world_window_height y_unit_vector = (pos[1] - world_window_y_center) / (self.world_window_height * 0.5) new_x_coord = x_unit_vector * self.tv_window_half_width * -1 new_y_coord = y_unit_vector * self.tv_window_half_height self.normalized_cursor_move(new_x_coord, new_y_coord) def normalized_cursor_move(self, new_x_coord, new_y_coord): try: while True: # always move in increments of 5, and move both x and y at once if possible x_step = 0 if int(new_x_coord) == self.tv_cursor_x else math.copysign(5, int(new_x_coord) - self.tv_cursor_x) y_step = 0 if int(new_y_coord) == self.tv_cursor_y else math.copysign(5, int(new_y_coord) - self.tv_cursor_y) if x_step == 0 and y_step == 0: return self.input_control.move(x_step, y_step) self.tv_cursor_x += x_step / 5 self.tv_cursor_y += y_step / 5 except Exception as e: print('pytv move failed with {}'.format(e)) self.input_control.connect_input() def connect_and_register_to_tv(self): # self.client = WebOSClient.discover()[0] self.client = WebOSClient(self.tv_context['local_address']) self.client.connect() register_status = self.client.register(store=self.tv_context) for status in register_status: if status == WebOSClient.PROMPTED: print("Please accept the connect on the TV!") elif status == WebOSClient.REGISTERED: print("Registration successful!") self.input_control = InputControl(self.client) self.input_control.connect_input() self.system_control = SystemControl(self.client) self.media_control = MediaControl(self.client) def click(self): # De-bounce the click click_time = int(round(time.time() * 1000)) if click_time < self.last_click_ms + 500: return self.last_click_ms = click_time # perform the click try: print('click emulated') self.input_control.click() except Exception as e: print('pytv click failed with {}'.format(e)) self.input_control.connect_input() def center(self): for i in range(18): tv.move(-20, -20) # time.sleep(0.01) for i in range(16): tv.move(15, 10) # time.sleep(0.01) def move(self, x, y): self.input_control.move(x, y) def toggle_pause(self): if self.paused: self.media_control.play() self.paused = False if self.verbose: self.system_control.notify("Play") else: self.media_control.pause() self.paused = True if self.verbose: self.system_control.notify("Pause") def keypad_up(self): self.input_control.up() def keypad_down(self): self.input_control.down() def keypad_left(self): self.input_control.left() def keypad_right(self): self.input_control.right() def keypad_ok(self): self.input_control.ok() if self.verbose: self.system_control.notify("Ok") def keypad_back(self): self.input_control.back() if self.verbose: self.system_control.notify("Back") def keypad_home(self): self.input_control.home() if self.verbose: self.system_control.notify("Home") def test_amazon_profiles(self): self.center() print('start') for i in range(9): self.move(-15, 0) time.sleep(1) self.center() for i in range(5): self.move(-15, 0) time.sleep(1) self.center() time.sleep(1) self.center() for i in range(6): self.move(15, 0) time.sleep(1) self.center() for i in range(8): self.move(15, 0) def test_multi_path(self): self.center() print('start') for i in range(8): self.move(-15, 0) self.system_control.notify("Path 1 complete") time.sleep(5) self.center() for i in range(10): self.move(-12, 0) self.system_control.notify("Path 2 complete") time.sleep(5) self.center() for i in range(12): self.move(-10, 0) self.system_control.notify("Path 3 complete") time.sleep(5) # Validates the TV width/height by moving cursor in a figure-eight pattern def test_figure_eight(self): # Right for i in range(85): tv.move(5, 0) time.sleep(.01) # Down for i in range(50): tv.move(0, 5) time.sleep(.01) # Left for i in range(85): tv.move(-5, 0) time.sleep(.01) # Up for i in range(50): tv.move(0, -5) time.sleep(.01) # Left for i in range(85): tv.move(-5, 0) time.sleep(.01) # Up for i in range(50): tv.move(0, -5) time.sleep(.01) # Right for i in range(85): tv.move(5, 0) time.sleep(.01) # Down for i in range(50): tv.move(0, 5) time.sleep(.01) def test_world_coordinate_translation(self): self.update_world_coordinate((0.0, 0.0)) time.sleep(1) self.update_world_coordinate((50.0, 0.0)) time.sleep(1) self.update_world_coordinate((50.0, 50.0)) time.sleep(1) self.update_world_coordinate((0.0, 50.0)) time.sleep(1) self.update_world_coordinate((0.0, 0.0)) time.sleep(1) self.update_world_coordinate((-100.0, 0.0)) time.sleep(1) self.update_world_coordinate((-200.0, 0.0)) time.sleep(1) self.update_world_coordinate((0.0, 200.0)) time.sleep(1)
send_magic_packet('<TV MAC ADDRESSES>') # The 'store' gets populated during the registration process. If it is empty, a registration prompt # will show up on the TV. You can pass any dictionary-like interface instead. store = {} client = WebOSClient("<TV IP>") client.connect() for status in client.register(store): if status == WebOSClient.PROMPTED: print("\nPlease accept the connect on the TV!") elif status == WebOSClient.REGISTERED: print("\nRegistration successful!") # print(store) media = MediaControl(client) system = SystemControl(client) inp = InputControl(client) def volume_set(): while True: print('\nWhat volume level do you want? [0-100]') try: # Ask the user what level they want the volume volume_level = int(input('> ')) if -1 < volume_level < 101: # Check that input is in valid range of 1-100 # Set the volume based on the user's input number media.set_volume(volume_level) else: print('Not a valid range, please use a range of 1-100.') continue
class TV: """All the TV controls wrapped in individual functions.""" _init_status = False def __init__(self): """Client key will be displayed on the TV when you accept the connection for the first time. Store the dict value as an env variable and use it as below. Using TV's ip makes the initial response much quicker but it looks for the TVs in ip range if an ip is not found.""" store = {'client_key': client_key} try: self.client = WebOSClient(ip_address) self.client.connect() except gaierror: sys.stdout.write( f"\rThe TV's IP has either changed or unreachable. Scanning your IP range.." ) self.client = WebOSClient.discover()[0] self.client.connect() except (TimeoutError, BrokenPipeError): sys.stdout.write( '\rOperation timed out. The TV might be turned off.') for status in self.client.register(store): if status == WebOSClient.REGISTERED and not TV._init_status: sys.stdout.write('\rConnected to the TV.') elif status == WebOSClient.PROMPTED: sys.stdout.write( '\rPlease accept the connection request on your TV.') # sys.stdout.write(f'\r{store}') # get the client key dict during the first run and store as in line #18 self.system = SystemControl(self.client) self.system.notify("Jarvis is controlling the TV now." ) if not TV._init_status else None self.media = MediaControl(self.client) self.app = ApplicationControl(self.client) self.source_control = SourceControl(self.client) TV._init_status = True def increase_volume(self): """Increases the volume by 10 units. Doesn't return anything""" for _ in range(10): self.media.volume_up() self.system.notify( f"Jarvis::Increased Volume: {self.media.get_volume()['volume']}%") def decrease_volume(self): """Decreases the volume by 10 units. Doesn't return anything""" for _ in range(10): self.media.volume_down() self.system.notify( f"Jarvis::Decreased Volume: {self.media.get_volume()['volume']}%") def get_volume(self): """Get volume status. Returns: {'scenario': 'mastervolume_tv_speaker', 'volume': 9, 'muted': False}""" self.system.notify( f"Jarvis::Current Volume: {self.media.get_volume()['volume']}%") return self.media.get_volume()['volume'] def set_volume(self, target): """The argument is an integer from 1 to 100. Doesn't return anything.""" self.media.set_volume(target) self.system.notify( f"Jarvis::Volume has been set to: {self.media.get_volume()['volume']}%" ) def mute(self): """status=True mutes the TV. status=False un-mutes it.""" self.system.notify(f"Jarvis::Muted") self.media.mute(True) def play(self): self.system.notify(f"Jarvis::Play") self.media.play() def pause(self): self.system.notify(f"Jarvis::Paused") self.media.pause() def stop(self): self.system.notify(f"Jarvis::Stop") self.media.stop() def rewind(self): self.system.notify(f"Jarvis::Rewind") self.media.rewind() def forward(self): self.system.notify(f"Jarvis::Forward") self.media.fast_forward() def audio_output(self): """Returns the currently used audio output source as AudioOutputSource instance""" media_output_source = self.media.get_audio_output() sys.stdout.write(f'{media_output_source}') def audio_output_source(self): """Returns a list of AudioOutputSource instances""" audio_outputs = self.media.list_audio_output_sources() sys.stdout.write(f'{audio_outputs}') return audio_outputs def set_audio_output_source(self): """AudioOutputSource instance""" # noinspection PyUnresolvedReferences self.media.set_audio_output(audio_output_source[0]) def list_apps(self): """Returns the list of available apps on the TV""" apps = self.app.list_apps() app_list = [app["title"] for app in apps] return app_list def launch_app(self, app_name): """launches an application""" app_launcher = [ x for x in self.app.list_apps() if app_name.lower() in x["title"].lower() ][0] return self.app.launch(app_launcher, content_id=None) def close_app(self, app_name): """closes a particular app using the launch_info received from launch_app()""" self.app.close(TV().launch_app(app_name)) def source(self): """Returns a list of InputSource instances""" sources = self.source_control.list_sources() sources_list = [source['label'] for source in sources] return sources_list def set_source(self, val): """Accepts an InputSource instance""" sources = self.source_control.list_sources() index = TV().source().index(val) self.source_control.set_source(sources[index]) return sources def current_app(self): """Returns the title of te current app that is running""" app_id = self.app.get_current( ) # Returns the application ID (string) of the foreground_app = [ x for x in self.app.list_apps() if app_id == x["id"] ][0] return foreground_app['title'] def shutdown(self): """Notifies the TV about shutdown and shuts down after 3 seconds""" self.system.notify(f'Jarvis::SHUTTING DOWN now') time.sleep(3) self.system.power_off()
def setVolume(self, volume): if self.mediaControl is None: self.mediaControl = MediaControl(self.get_client()) self.mediaControl.set_volume(volume)
def register_volume_control(self, callback): lg = self.connect() media = MediaControl(lg) RxvLogger.debug("callback registered") media.subscribe_get_volume(callback)