Ejemplo n.º 1
0
    def test_registration(self):
        client = WebOSClient("test-host")
        sent_event = Event()

        def make_response(prompt, registered, wrong):
            def send_response():
                sent_event.wait()
                sent_event.clear()

                if prompt:
                    client.received_message(
                        json.dumps({
                            "id": "1",
                            "payload": {
                                "pairingType": "PROMPT"
                            }
                        }))
                if registered:
                    client.received_message(
                        json.dumps({
                            "id": "1",
                            "payload": {
                                "client-key": "xyz"
                            },
                            "type": "registered"
                        }))
                if wrong:
                    client.received_message(
                        json.dumps({
                            "id": "1",
                            "type": "wrong-response"
                        }))

            return send_response

        def patched_send(*args, **kwargs):
            obj = WebOSClient.send(client, unique_id="1", *args, **kwargs)
            sent_event.set()
            return obj

        client.send = patched_send

        store = {}
        Thread(target=make_response(True, True, False)).start()
        gen = client.register(store, timeout=10)
        assert next(gen) == WebOSClient.PROMPTED
        assert next(gen) == WebOSClient.REGISTERED

        assert store == {"client_key": "xyz"}

        # Test with non-empty store.
        Thread(target=make_response(False, True, False)).start()
        list(client.register(store, timeout=10)) == [WebOSClient.REGISTERED]
        assert "xyz" in self.sent_message

        # Test wrong response.
        Thread(target=make_response(False, False, True)).start()
        with raises(Exception):
            list(client.register(store, timeout=10))
Ejemplo n.º 2
0
class TV:
    def __init__(self, ip, key=None):
        self.store = {}
        if key:
            self.store['client_key'] = key
        self.ip = ip
        self.client = WebOSClient(ip)
        self.connnected = False
        self.clients = {}

    def connect(self):
        self.client.connect()
        list(self.client.register(self.store))
        self.connnected = True

    def command(self, control, command, args=None):
        args = args or []
        if not self.connnected:
            self.connect()
        control = self.get_control(control)
        return getattr(control, command)(*args)

    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]
Ejemplo n.º 3
0
class TV:
    def __init__(self, ip, key=None):
        self.store = {}
        if key:
            self.store['client_key'] = key
        self.ip = ip
        self.client = WebOSClient(ip)
        self.connnected = False
        self.clients = {}

    def connect(self):
        self.client.connect()
        list(self.client.register(self.store))
        self.connnected = True

    def command(self, control, command, args=None):
        args = args or []
        if not self.connnected:
            self.connect()
        control = self.get_control(control)
        return getattr(control, command)(*args)

    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]
Ejemplo n.º 4
0
    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")
Ejemplo n.º 5
0
    def connect(self):

        if not self.is_running():
            self.start()
        RxvLogger.debug("TV connect ip:" + self.ip)
        for i in range(0, 10):
            try:
                lg = WebOSClient(self.ip)
                lg.connect()
            except Exception as e:
                RxvLogger.error("Unable connect TV, retry e: " + str(e))
                time.sleep(3)
            else:
                break
        try:
            for status in lg.register(self.store):
                if status == WebOSClient.PROMPTED:
                    RxvLogger.log("Please accept the connect on the TV!")
                elif status == WebOSClient.REGISTERED:
                    RxvLogger.log("Registration successful!")
                    lg_sys = SystemControl(lg)
                    lg_sys.notify("RxvLG: Registration successful!")
            self.save_store()
            return lg
        except Exception as e:
            RxvLogger.error("Registration failed exiting!")
            RxvLogger.debug("Exception" + str(e))
Ejemplo n.º 6
0
class S(object):
    " Store class to delay instantiation "
    config = None

    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)
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
def main():
    parser = ArgumentParser(description='LG WebOS CLI wrapper',
                            formatter_class=RawTextHelpFormatter)
    parser.add_argument('method',
                        type=str,
                        help=format_controls_help(controls_subsystems))
    parser.add_argument('method_args',
                        metavar='arguments',
                        nargs='*',
                        help='Method arguments ([arg])')
    args = parser.parse_args(None if sys.argv[1:] else ['-h'])

    if args.method == 'power_on':
        # TODO figure out how to enumerate network interfaces and find broadcast ip addr (netifaces?)
        send_magic_packet(ConnectionCache.read_mac(),
                          ip_address='192.168.15.255')
        sys.exit()

    addr = ConnectionCache.read_addr()
    if not addr:
        clients = {
            str(index): client
            for index, client in enumerate(WebOSClient.discover(), 1)
        }
        entered = None
        while entered is None or entered not in clients.keys():
            entered = input('\n'.join([
                *[') '.join([i, c.host]) for i, c in clients.items()],
                '(q to quit)\n',
            ]))
            if entered == 'q':
                sys.exit()
        client = clients.get(entered)
    else:
        client = WebOSClient(addr)
    client.connect()
    store = CredStorage.load(client.host)
    for status in client.register(store):
        if status == WebOSClient.PROMPTED:
            print('Please accept the connection on the TV')
            ConnectionCache.write(client)
        if status == WebOSClient.REGISTERED:
            CredStorage.persist(client.host, store)
    if args.method not in controls:
        parser.print_help()
        sys.exit()

    stdout.write(str(call(client, args.method, args.method_args)))
Ejemplo n.º 9
0
    def authenticate(self, friendly_name: str, host: str, path: str):
        settings = self.settings
        store = {}
        friendly_name = friendly_name or host

        client = WebOSClient(host)
        client.connect()
        prompted = False
        for status in client.register(store):
            if status == WebOSClient.PROMPTED:
                prompted = True
        if not prompted:
            raise ClientError(f'Unable to authenticate with TV {host}.')
        if 'client_key' not in store:
            raise ClientError(f'Client key not retrieved from TV {host}.')
        client_key = store['client_key']
        settings.set(friendly_name, host, client_key)
        settings.serialize(path)
Ejemplo n.º 10
0
    def create(self, path: str, friendly_name: str) -> WebOSClient:
        self.settings.load(path)
        settings = self.settings.get(friendly_name)
        try:
            host = settings.host
        except KeyError:
            raise ClientError('Missing TV IP address. Please authenticate with your TV.') from None
        try:
            store = {'client_key': settings.client_key}
        except KeyError:
            raise ClientError('Missing client key. Please authenticate with your TV.') from None

        client = WebOSClient(host)
        client.connect()
        status = client.register(store)
        if not all([s == WebOSClient.REGISTERED for s in status]):
            raise ClientError(f'Error connecting to TV {host}. Please authenticate again.')
        return client
Ejemplo n.º 11
0
def init(wakeup=True):
    if not os.path.exists(STORE):
        store = {}
    else:
        with open(STORE) as fh:
            store = json.load(fh)

    if "ipaddr" in store:
        client = WebOSClient(store["ipaddr"])
    else:
        clients = WebOSClient.discover()
        client = clients[0]

    try:
        client.connect()
    except OSError:
        if wakeup and "macaddr" in store:
            print("Starting TV")
            send_magic_packet(store["macaddr"])
            time.sleep(10)
            return init(False)
        print("TV is off", file=sys.stderr)
        sys.exit(1)

    for status in client.register(store):
        if status == WebOSClient.PROMPTED:
            print("Please accept the connect on the TV!")
        elif status == WebOSClient.REGISTERED:
            pass

    store["ipaddr"] = client.host
    macaddr = get_macaddr(store["ipaddr"])

    if macaddr:
        store["macaddr"] = macaddr

    with open(STORE, "w") as out:
        json.dump(store, out)

    return client
Ejemplo n.º 12
0
    def _device_connect(name: str,
                        ip: HostType,
                        key: str = None) -> LGTVCommander:
        """
        Connect to a WebOS LGTV client, check pairing status and attempt pairing if not paired.

        :param name: The human-readable name of the device
        :param ip:   The IP address for the device
        :param key:  The pairing key provided by the device. None if not paired.
        :return: The connected & registered client.
        """
        client = WebOSClient(ip)
        try:
            client.connect()

            store = {} if key is None else {'client_key': key}

            if key is None:
                log.logger.info(
                    f"LGTV {name} is not paired - attempting to pair.")

            has_registered = False
            client_status = None
            for client_status in client.register(store):
                has_registered |= client_status == WebOSClient.REGISTERED

            if not has_registered:
                raise LGTVException(
                    f"Could not pair with LGTV {name} [Status: {client_status}]."
                )

            return LGTVCommander(client)

        except TimeoutError:
            raise LGTVException(f"Timed out connecting to LGTV {name}.")
        except Exception as e:
            if str(e) == "Failed to register.":
                raise LGTVException(f"Failed to pair LGTV {name}.")
            raise e
Ejemplo n.º 13
0
class Client(object):
    @staticmethod
    def load_config(configFile="~/.config/lgtv"):
        log.debug("Loading config from " + configFile)
        if os.path.exists(os.path.expanduser(configFile)):
            with open(os.path.expanduser(configFile), 'r') as fp:
                config = json.load(fp)

            log.debug(config)
            return config

    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 __on_app_changed(self, status, payload):
        self.log.debug(f'app: {payload}')
        self.current = self.apps[payload]

    def __on_volume_changed(self, status, payload):
        self.log.debug(f'volume :{payload}')
        self.volume = payload
Ejemplo n.º 14
0
from wakeonlan import send_magic_packet
from pywebostv.discovery import discover
from pywebostv.connection import WebOSClient
from pywebostv.controls import MediaControl, SystemControl, InputControl

# Send a wake on lan broadcast to your TV.
# This will ensure the TV is on, before trying to connect.
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
Ejemplo n.º 15
0
 def test_registration_timeout(self):
     client = WebOSClient("test-host")
     with raises(Exception):
         list(client.register({}, timeout=5))
Ejemplo n.º 16
0
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()
Ejemplo n.º 17
0
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)