From 0cf5e36bdde70491eace96bb63fa3170cad486f5 Mon Sep 17 00:00:00 2001 From: Keith Prickett Date: Wed, 28 Nov 2018 16:41:10 -0800 Subject: [PATCH 01/13] VIC-11462 Python SDK: clamp head angle (#21) * Clamped the head angle between -22 and 45 degrees. * Exposed the MIN and MAX head angles * Added comparison operators to Angle * Cleaned up example code --- anki_vector/behavior.py | 32 ++++++++++++++++++++++---------- anki_vector/util.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/anki_vector/behavior.py b/anki_vector/behavior.py index 632797c..c904050 100644 --- a/anki_vector/behavior.py +++ b/anki_vector/behavior.py @@ -34,8 +34,9 @@ functions for all the behaviors. """ -__all__ = ["BehaviorComponent", "MAX_HEAD_ANGLE", "MAX_LIFT_HEIGHT", "MAX_LIFT_HEIGHT_MM", - "MIN_HEAD_ANGLE", "MIN_LIFT_HEIGHT", "MIN_LIFT_HEIGHT_MM"] +__all__ = ["MAX_HEAD_ANGLE", "MIN_HEAD_ANGLE", + "MAX_LIFT_HEIGHT", "MAX_LIFT_HEIGHT_MM", "MIN_LIFT_HEIGHT", "MIN_LIFT_HEIGHT_MM", + "BehaviorComponent"] from . import connection, objects, util @@ -43,13 +44,11 @@ # Constants -#: The minimum angle the robot's head can be set to -# TODO Clamp to this value. -MIN_HEAD_ANGLE = util.degrees(-22) +#: The minimum angle the robot's head can be set to. +MIN_HEAD_ANGLE = util.degrees(-22.0) #: The maximum angle the robot's head can be set to -# TODO Clamp to this value. -MAX_HEAD_ANGLE = util.degrees(45) +MAX_HEAD_ANGLE = util.degrees(45.0) # The lowest height-above-ground that lift can be moved to in millimeters. MIN_LIFT_HEIGHT_MM = 32.0 @@ -390,7 +389,6 @@ async def turn_in_place(self, return await self.grpc_interface.TurnInPlace(turn_in_place_request) - # TODO Clamp angle to MIN_HEAD_ANGLE and MAX_HEAD_ANGLE. @connection.on_connection_thread() async def set_head_angle(self, angle: util.Angle, @@ -402,6 +400,7 @@ async def set_head_angle(self, :param angle: Desired angle for Vector's head. (:const:`MIN_HEAD_ANGLE` to :const:`MAX_HEAD_ANGLE`). + (we clamp it to this range internally). :param accel: Acceleration of Vector's head in radians per second squared. :param max_speed: Maximum speed of Vector's head in radians per second. :param duration: Time for Vector's head to move in seconds. A value @@ -415,11 +414,24 @@ async def set_head_angle(self, import anki_vector from anki_vector.util import degrees + from anki_vector.behavior import MIN_HEAD_ANGLE, MAX_HEAD_ANGLE with anki_vector.Robot() as robot: - robot.behavior.set_head_angle(degrees(-22.0)) - robot.behavior.set_head_angle(degrees(45.0)) + # move head from minimum to maximum angle + robot.behavior.set_head_angle(MIN_HEAD_ANGLE) + robot.behavior.set_head_angle(MAX_HEAD_ANGLE) + # move head to middle + robot.behavior.set_head_angle(degrees(35.0)) """ + if angle < MIN_HEAD_ANGLE: + self.logger.warning("head angle %s too small, should be in %f..%f range - clamping", + angle.degrees, MIN_HEAD_ANGLE.degrees, MAX_HEAD_ANGLE.degrees) + angle = MIN_HEAD_ANGLE + elif angle > MAX_HEAD_ANGLE: + self.logger.warning("head angle %s too large, should be in %f..%f range - clamping", + angle.degrees, MIN_HEAD_ANGLE.degrees, MAX_HEAD_ANGLE.degrees) + angle = MAX_HEAD_ANGLE + set_head_angle_request = protocol.SetHeadAngleRequest(angle_rad=angle.radians, max_speed_rad_per_sec=max_speed, accel_rad_per_sec2=accel, diff --git a/anki_vector/util.py b/anki_vector/util.py index 6a5a670..98c1b4e 100644 --- a/anki_vector/util.py +++ b/anki_vector/util.py @@ -374,6 +374,42 @@ def __mul__(self, other): raise TypeError("Unsupported type for * expected number") return Angle(radians=(self.radians * other)) + def __truediv__(self, other): + if not isinstance(other, (int, float)): + raise TypeError("Unsupported type for / expected number") + return radians(self.radians / other) + + def _cmp_int(self, other): + if not isinstance(other, Angle): + raise TypeError("Unsupported type for comparison expected Angle") + return self.radians - other.radians + + def __eq__(self, other): + return self._cmp_int(other) == 0 + + def __ne__(self, other): + return self._cmp_int(other) != 0 + + def __gt__(self, other): + return self._cmp_int(other) > 0 + + def __lt__(self, other): + return self._cmp_int(other) < 0 + + def __ge__(self, other): + return self._cmp_int(other) >= 0 + + def __le__(self, other): + return self._cmp_int(other) <= 0 + + @property + def abs_value(self): + """:class:`anki_vector.util.Angle`: The absolute value of the angle. + + If the Angle is positive then it returns a copy of this Angle, otherwise it returns -Angle. + """ + return Angle(radians = abs(self._radians)) + def angle_z_to_quaternion(angle_z: Angle): """This function converts an angle in the z axis (Euler angle z component) to a quaternion. From 86ce4c15f7247e7afbf2ba09a674f25e83f8fba5 Mon Sep 17 00:00:00 2001 From: Michelle Sintov Date: Wed, 28 Nov 2018 17:20:50 -0800 Subject: [PATCH 02/13] VIC-11186 Update Linux steps and other minor cleanup #22 --- docs/source/getstarted.rst | 10 ++-- docs/source/install-linux.rst | 61 +++++++++++++++++++++++-- docs/source/install-macos.rst | 1 - docs/source/install-windows.rst | 1 - docs/source/troubleshooting.rst | 1 - examples/tutorials/07_dock_with_cube.py | 1 + 6 files changed, 64 insertions(+), 11 deletions(-) diff --git a/docs/source/getstarted.rst b/docs/source/getstarted.rst index f115b8c..71ea479 100644 --- a/docs/source/getstarted.rst +++ b/docs/source/getstarted.rst @@ -26,7 +26,6 @@ Prerequisites ------------- * You have completed the Installation steps, found here: :ref:`initial` -* You have updated Vector to OS 1.0.1 or later. Check the OS version by putting Vector on the charger, double-tapping his backpack button, and raising and lowering his arms once. * The Vector companion app is *not* currently connected to Vector. * Vector is connected to the same network as your computer. * You can see Vector's eyes on his screen. @@ -65,11 +64,11 @@ First, change to the ``tutorials`` sub-directory of the ``examples`` directory. Then, run the program. - a. For macOS and Linux systems, type the following and press **Enter**:: + a. For macOS and Ubuntu 18.04 systems, type the following and press **Enter**:: ./01_hello_world.py - The same can also be achieved on macOS/Linux with:: + The same can also be achieved with:: python3 01_hello_world.py @@ -77,6 +76,11 @@ Then, run the program. py 01_hello_world.py + c. For Ubuntu 16.04 systems, type the following and press **Enter**:: + + python3.6 01_hello_world.py + + 2. If done correctly, Vector will say "Hello, World!" .. warning:: If Vector does not perform as expected, look at the first Terminal window and make sure no error messages appeared. If you continue to have issues, please seek help in the Forums. diff --git a/docs/source/install-linux.rst b/docs/source/install-linux.rst index 6e4afdb..176bb2b 100644 --- a/docs/source/install-linux.rst +++ b/docs/source/install-linux.rst @@ -11,7 +11,6 @@ Prerequisites * Vector is powered on. * You have successfully created an Anki account. * Vector has been set up with the Vector companion app. -* You have updated Vector to OS 1.0.1 or later. Check the OS version by putting Vector on the charger, double-tapping his backpack button, and raising and lowering his arms once. * The Vector companion app is *not* currently connected to Vector. * Vector is connected to the same network as your computer. * You can see Vector's eyes on his screen. @@ -19,16 +18,16 @@ Prerequisites This guide provides instructions on installing the Vector SDK for computers running with an Ubuntu Linux operating system. -.. warning:: The Vector SDK is tested and and supported on Ubuntu 16.04 and 18.04. Anki makes no guarantee the Vector SDK will work on other versions of Linux. If you wish to try the Vector SDK on versions of Linux *other than* Ubuntu 16.04 or 18.04, please ensure the following dependencies are installed: +.. warning:: The Vector SDK is tested and and supported on Ubuntu 18.04 and 16.04. Anki makes no guarantee the Vector SDK will work on other versions of Linux. If you wish to try the Vector SDK on versions of Linux *other than* Ubuntu 18.04 or 16.04, please ensure the following dependencies are installed: * Python 3.6.1 or later * pip for Python 3 (Python package installer) -^^^^^^^^^^^^^^^^^^^^^^ -Ubuntu 16.04 and 18.04 -^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^ +Ubuntu 18.04 +^^^^^^^^^^^^ """"""""""""""""""" Python Installation @@ -69,6 +68,58 @@ You will see "SUCCESS!" when this script successfully completes. .. warning:: These credentials give full access to your robot, including camera stream, audio stream and data. Do not share these credentials. + + +^^^^^^^^^^^^ +Ubuntu 16.04 +^^^^^^^^^^^^ + +"""""""""""""""""""""""""""""" +Python and Module Installation +"""""""""""""""""""""""""""""" + +1. Type the following into your Terminal window to install Python 3.6:: + + sudo add-apt-repository ppa:deadsnakes/ppa + sudo apt-get update + sudo apt-get install python3.6 + +2. Then install pip by typing in the following into the Terminal window:: + + sudo apt install python3-pip + +3. Install the following additional packages:: + + sudo apt-get install build-essential libssl-dev libffi-dev python3.6-dev + + +"""""""""""""""" +SDK Installation +"""""""""""""""" + +To install the SDK, type the following into the Terminal window:: + + cd vector_python_sdk_0.5.0 + python3.6 -m pip install . + +^^^^^^^^^^^^^^^^^^^^^ +Vector Authentication +^^^^^^^^^^^^^^^^^^^^^ + +To authenticate with the robot, type the following into the Terminal window:: + + cd vector_python_sdk_0.5.0 + python3.6 configure.py + +You will be prompted for your robot's name, ip address and serial number. You will also be asked for your Anki login and password. Make sure to use the same account that was used to set up your Vector. + +You will see "SUCCESS!" when this script successfully completes. + +.. note:: Running ``configure.py`` will automatically download the Vector robot certificate to your computer and store credentials to allow you to connect to Vector. These credentials will be stored under your home directory in folder ``.anki_vector``. + +.. warning:: These credentials give full access to your robot, including camera stream, audio stream and data. Do not share these credentials. + + ^^^^^^^^^^^^^^^ Troubleshooting ^^^^^^^^^^^^^^^ diff --git a/docs/source/install-macos.rst b/docs/source/install-macos.rst index 44a868a..6a590d7 100644 --- a/docs/source/install-macos.rst +++ b/docs/source/install-macos.rst @@ -13,7 +13,6 @@ Prerequisites * Vector is powered on. * You have successfully created an Anki account. * Vector has been set up with the Vector companion app. -* You have updated Vector to OS 1.0.1 or later. Check the OS version by putting Vector on the charger, double-tapping his backpack button, and raising and lowering his arms once. * The Vector companion app is *not* currently connected to Vector. * Vector is connected to the same network as your computer. * You can see Vector's eyes on his screen. diff --git a/docs/source/install-windows.rst b/docs/source/install-windows.rst index 54ccd89..a86b5dc 100644 --- a/docs/source/install-windows.rst +++ b/docs/source/install-windows.rst @@ -13,7 +13,6 @@ Prerequisites * Vector is powered on. * You have successfully created an Anki account. * Vector has been set up with the Vector companion app. -* You have updated Vector to OS 1.0.1 or later. Check the OS version by putting Vector on the charger, double-tapping his backpack button, and raising and lowering his arms once. * The Vector companion app is *not* currently connected to Vector. * Vector is connected to the same network as your computer. * You can see Vector's eyes on his screen. diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index e566e76..adeba99 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -48,7 +48,6 @@ The prerequisites to run ``configure.py`` are: * Vector is powered on. * You have successfully created an Anki account. * Vector has been set up with the Vector companion app. -* You have updated Vector to OS 1.0.1 or later. Check the OS version by putting Vector on the charger, double-tapping his backpack button, and raising and lowering his arms once. * The Vector companion app is *not* currently connected to Vector. * Vector is connected to the same network as your computer. * You can see Vector's eyes on his screen. diff --git a/examples/tutorials/07_dock_with_cube.py b/examples/tutorials/07_dock_with_cube.py index 3f36366..104388c 100755 --- a/examples/tutorials/07_dock_with_cube.py +++ b/examples/tutorials/07_dock_with_cube.py @@ -38,6 +38,7 @@ def main(): robot.behavior.set_head_angle(degrees(-5.0)) robot.behavior.set_lift_height(0.0) + print("Connecting to a cube...") robot.world.connect_cube() if robot.world.connected_light_cube: From ccef24c85a8c5d76bef16b6864ff4e8976673289 Mon Sep 17 00:00:00 2001 From: Keith Prickett Date: Fri, 30 Nov 2018 17:02:59 -0800 Subject: [PATCH 03/13] VIC-12133 pull up latest proto (#28) * VIC-12133 pull up latest proto Preparation for VIC-7776 which needs a new enum. * VIC-12133 Update proto with enum for RobotStatus Clean up warning in proto update output --- anki_vector/messaging/messages.proto | 23 +- anki_vector/messaging/messages_pb2.py | 615 ++++++++++-------- anki_vector/messaging/onboarding_steps.proto | 20 - anki_vector/messaging/onboarding_steps_pb2.py | 30 - .../messaging/onboarding_steps_pb2_grpc.py | 3 - anki_vector/messaging/settings.proto | 1 - anki_vector/messaging/settings_pb2.py | 14 +- 7 files changed, 388 insertions(+), 318 deletions(-) delete mode 100644 anki_vector/messaging/onboarding_steps.proto delete mode 100644 anki_vector/messaging/onboarding_steps_pb2.py delete mode 100644 anki_vector/messaging/onboarding_steps_pb2_grpc.py diff --git a/anki_vector/messaging/messages.proto b/anki_vector/messaging/messages.proto index fee32c2..5792cbe 100644 --- a/anki_vector/messaging/messages.proto +++ b/anki_vector/messaging/messages.proto @@ -20,7 +20,6 @@ package Anki.Vector.external_interface; import "anki_vector/messaging/response_status.proto"; import "anki_vector/messaging/extensions.proto"; -import "anki_vector/messaging/onboarding_steps.proto"; // A null message used by streams to verify that the client is // still connected. @@ -176,6 +175,28 @@ message RobotState { TouchData touch_data = 17; } +enum RobotStatus +{ + ROBOT_STATUS_NONE = 0x0; + ROBOT_STATUS_IS_MOVING = 0x1; + ROBOT_STATUS_IS_CARRYING_BLOCK = 0x2; + ROBOT_STATUS_IS_PICKING_OR_PLACING = 0x4; + ROBOT_STATUS_IS_PICKED_UP = 0x8; + ROBOT_STATUS_IS_BUTTON_PRESSED = 0x10; + ROBOT_STATUS_IS_FALLING = 0x20; + ROBOT_STATUS_IS_ANIMATING = 0x40; + ROBOT_STATUS_IS_PATHING = 0x80; + ROBOT_STATUS_LIFT_IN_POS = 0x100; + ROBOT_STATUS_HEAD_IN_POS = 0x200; + ROBOT_STATUS_CALM_POWER_MODE = 0x400; + ROBOT_STATUS_IS_ON_CHARGER = 0x1000; + ROBOT_STATUS_IS_CHARGING = 0x2000; + ROBOT_STATUS_CLIFF_DETECTED = 0x4000; + ROBOT_STATUS_ARE_WHEELS_MOVING = 0x8000; + ROBOT_STATUS_IS_BEING_HELD = 0x10000; + ROBOT_STATUS_IS_MOTION_DETECTED = 0x20000; +} + message CladPoint { float x = 1; diff --git a/anki_vector/messaging/messages_pb2.py b/anki_vector/messaging/messages_pb2.py index c29df6b..7133b77 100644 --- a/anki_vector/messaging/messages_pb2.py +++ b/anki_vector/messaging/messages_pb2.py @@ -16,17 +16,103 @@ from anki_vector.messaging import response_status_pb2 as anki__vector_dot_messaging_dot_response__status__pb2 from anki_vector.messaging import extensions_pb2 as anki__vector_dot_messaging_dot_extensions__pb2 -from anki_vector.messaging import onboarding_steps_pb2 as anki__vector_dot_messaging_dot_onboarding__steps__pb2 DESCRIPTOR = _descriptor.FileDescriptor( name='anki_vector/messaging/messages.proto', package='Anki.Vector.external_interface', syntax='proto3', - serialized_pb=_b('\n$anki_vector/messaging/messages.proto\x12\x1e\x41nki.Vector.external_interface\x1a+anki_vector/messaging/response_status.proto\x1a&anki_vector/messaging/extensions.proto\x1a,anki_vector/messaging/onboarding_steps.proto\"\x0f\n\rKeepAlivePing\"\x19\n\tAnimation\x12\x0c\n\x04name\x18\x01 \x01(\t\"|\n\x12\x44riveWheelsRequest\x12\x17\n\x0fleft_wheel_mmps\x18\x01 \x01(\x02\x12\x18\n\x10right_wheel_mmps\x18\x02 \x01(\x02\x12\x18\n\x10left_wheel_mmps2\x18\x03 \x01(\x02\x12\x19\n\x11right_wheel_mmps2\x18\x04 \x01(\x02\"U\n\x13\x44riveWheelsResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\",\n\x0fMoveHeadRequest\x12\x19\n\x11speed_rad_per_sec\x18\x01 \x01(\x02\"R\n\x10MoveHeadResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\",\n\x0fMoveLiftRequest\x12\x19\n\x11speed_rad_per_sec\x18\x01 \x01(\x02\"R\n\x10MoveLiftResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\xb4\x01\n\x14PlayAnimationRequest\x12<\n\tanimation\x18\x01 \x01(\x0b\x32).Anki.Vector.external_interface.Animation\x12\r\n\x05loops\x18\x02 \x01(\r\x12\x19\n\x11ignore_body_track\x18\x03 \x01(\x08\x12\x19\n\x11ignore_head_track\x18\x04 \x01(\x08\x12\x19\n\x11ignore_lift_track\x18\x05 \x01(\x08\"\x98\x01\n\x15PlayAnimationResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12?\n\x06result\x18\x02 \x01(\x0e\x32/.Anki.Vector.external_interface.BehaviorResults\"\x17\n\x15ListAnimationsRequest\"\x9c\x01\n\x16ListAnimationsResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x42\n\x0f\x61nimation_names\x18\x02 \x03(\x0b\x32).Anki.Vector.external_interface.Animation\"_\n\x1a\x44isplayFaceImageRGBRequest\x12\x11\n\tface_data\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64uration_ms\x18\x02 \x01(\r\x12\x19\n\x11interrupt_running\x18\x03 \x01(\x08\"]\n\x1b\x44isplayFaceImageRGBResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x1b\n\x19MeetVictorFaceScanStarted\"\x1c\n\x1aMeetVictorFaceScanComplete\"\xbf\x02\n\x06Status\x12\x62\n\x1dmeet_victor_face_scan_started\x18\x02 \x01(\x0b\x32\x39.Anki.Vector.external_interface.MeetVictorFaceScanStartedH\x00\x12\x64\n\x1emeet_victor_face_scan_complete\x18\x03 \x01(\x0b\x32:.Anki.Vector.external_interface.MeetVictorFaceScanCompleteH\x00\x12\\\n\x19\x66\x61\x63\x65_enrollment_completed\x18\x04 \x01(\x0b\x32\x37.Anki.Vector.external_interface.FaceEnrollmentCompletedH\x00\x42\r\n\x0bstatus_type\"\xac\x01\n\x08WakeWord\x12H\n\x0fwake_word_begin\x18\x01 \x01(\x0b\x32-.Anki.Vector.external_interface.WakeWordBeginH\x00\x12\x44\n\rwake_word_end\x18\x02 \x01(\x0b\x32+.Anki.Vector.external_interface.WakeWordEndH\x00\x42\x10\n\x0ewake_word_type\"b\n\x11TimeStampedStatus\x12\x36\n\x06status\x18\x01 \x01(\x0b\x32&.Anki.Vector.external_interface.Status\x12\x15\n\rtimestamp_utc\x18\x02 \x01(\r\"p\n\nPoseStruct\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\x12\n\n\x02q0\x18\x04 \x01(\x02\x12\n\n\x02q1\x18\x05 \x01(\x02\x12\n\n\x02q2\x18\x06 \x01(\x02\x12\n\n\x02q3\x18\x07 \x01(\x02\x12\x11\n\torigin_id\x18\x08 \x01(\r\",\n\tAccelData\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\"+\n\x08GyroData\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\"\xa3\x01\n\x08ProxData\x12\x13\n\x0b\x64istance_mm\x18\x01 \x01(\r\x12\x16\n\x0esignal_quality\x18\x02 \x01(\x02\x12\x19\n\x11is_in_valid_range\x18\x03 \x01(\x08\x12\x1f\n\x17is_valid_signal_quality\x18\x04 \x01(\x08\x12\x16\n\x0eis_lift_in_fov\x18\x05 \x01(\x08\x12\x16\n\x0eis_too_pitched\x18\x06 \x01(\x08\">\n\tTouchData\x12\x17\n\x0fraw_touch_value\x18\x01 \x01(\r\x12\x18\n\x10is_being_touched\x18\x02 \x01(\x08\"\x82\x05\n\nRobotState\x12\x38\n\x04pose\x18\x01 \x01(\x0b\x32*.Anki.Vector.external_interface.PoseStruct\x12\x16\n\x0epose_angle_rad\x18\x02 \x01(\x02\x12\x16\n\x0epose_pitch_rad\x18\x03 \x01(\x02\x12\x1d\n\x15left_wheel_speed_mmps\x18\x04 \x01(\x02\x12\x1e\n\x16right_wheel_speed_mmps\x18\x05 \x01(\x02\x12\x16\n\x0ehead_angle_rad\x18\x06 \x01(\x02\x12\x16\n\x0elift_height_mm\x18\x07 \x01(\x02\x12\x38\n\x05\x61\x63\x63\x65l\x18\x08 \x01(\x0b\x32).Anki.Vector.external_interface.AccelData\x12\x36\n\x04gyro\x18\t \x01(\x0b\x32(.Anki.Vector.external_interface.GyroData\x12\x1a\n\x12\x63\x61rrying_object_id\x18\n \x01(\x05\x12!\n\x19\x63\x61rrying_object_on_top_id\x18\x0b \x01(\x05\x12\x1f\n\x17head_tracking_object_id\x18\x0c \x01(\x05\x12\x1e\n\x16localized_to_object_id\x18\r \x01(\x05\x12\x1d\n\x15last_image_time_stamp\x18\x0e \x01(\r\x12\x0e\n\x06status\x18\x0f \x01(\r\x12;\n\tprox_data\x18\x10 \x01(\x0b\x32(.Anki.Vector.external_interface.ProxData\x12=\n\ntouch_data\x18\x11 \x01(\x0b\x32).Anki.Vector.external_interface.TouchData\"!\n\tCladPoint\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\"Q\n\x08\x43ladRect\x12\x12\n\nx_top_left\x18\x01 \x01(\x02\x12\x12\n\ny_top_left\x18\x02 \x01(\x02\x12\r\n\x05width\x18\x03 \x01(\x02\x12\x0e\n\x06height\x18\x04 \x01(\x02\"\x8a\x04\n\x11RobotObservedFace\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x01 \x01(\x05\x12\x11\n\ttimestamp\x18\x02 \x01(\r\x12\x38\n\x04pose\x18\x03 \x01(\x0b\x32*.Anki.Vector.external_interface.PoseStruct\x12:\n\x08img_rect\x18\x04 \x01(\x0b\x32(.Anki.Vector.external_interface.CladRect\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x44\n\nexpression\x18\x06 \x01(\x0e\x32\x30.Anki.Vector.external_interface.FacialExpression\x12\x19\n\x11\x65xpression_values\x18\x07 \x03(\r\x12;\n\x08left_eye\x18\x08 \x03(\x0b\x32).Anki.Vector.external_interface.CladPoint\x12<\n\tright_eye\x18\t \x03(\x0b\x32).Anki.Vector.external_interface.CladPoint\x12\x37\n\x04nose\x18\n \x03(\x0b\x32).Anki.Vector.external_interface.CladPoint\x12\x38\n\x05mouth\x18\x0b \x03(\x0b\x32).Anki.Vector.external_interface.CladPoint\"<\n\x1aRobotChangedObservedFaceID\x12\x0e\n\x06old_id\x18\x01 \x01(\x05\x12\x0e\n\x06new_id\x18\x02 \x01(\x05\"1\n\x10\x41ppIntentRequest\x12\x0e\n\x06intent\x18\x01 \x01(\t\x12\r\n\x05param\x18\x02 \x01(\t\"\x11\n\x0f\x41ppDisconnected\"S\n\x11\x41ppIntentResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"~\n\x17\x46\x61\x63\x65\x45nrollmentCompleted\x12\x44\n\x06result\x18\x01 \x01(\x0e\x32\x34.Anki.Vector.external_interface.FaceEnrollmentResult\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"\x1d\n\x1b\x43\x61ncelFaceEnrollmentRequest\"^\n\x1c\x43\x61ncelFaceEnrollmentResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x1d\n\x1bRequestEnrolledNamesRequest\"\xc2\x01\n\x0fLoadedKnownFace\x12$\n\x1cseconds_since_first_enrolled\x18\x01 \x01(\x03\x12\"\n\x1aseconds_since_last_updated\x18\x02 \x01(\x03\x12\x1f\n\x17seconds_since_last_seen\x18\x03 \x01(\x03\x12%\n\x1dlast_seen_seconds_since_epoch\x18\x04 \x01(\x03\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x05 \x01(\x05\x12\x0c\n\x04name\x18\x06 \x01(\t\"9\n\x18RobotRenamedEnrolledFace\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x01 \x01(\x05\x12\x0c\n\x04name\x18\x02 \x01(\t\"\x9e\x01\n\x1cRequestEnrolledNamesResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12>\n\x05\x66\x61\x63\x65s\x18\x02 \x03(\x0b\x32/.Anki.Vector.external_interface.LoadedKnownFace\"T\n\x1dUpdateEnrolledFaceByIDRequest\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x01 \x01(\x05\x12\x10\n\x08old_name\x18\x02 \x01(\t\x12\x10\n\x08new_name\x18\x03 \x01(\t\"`\n\x1eUpdateEnrolledFaceByIDResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"/\n\x1c\x45raseEnrolledFaceByIDRequest\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x01 \x01(\x05\"_\n\x1d\x45raseEnrolledFaceByIDResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x1e\n\x1c\x45raseAllEnrolledFacesRequest\"_\n\x1d\x45raseAllEnrolledFacesResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x88\x01\n\x16SetFaceToEnrollRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0bobserved_id\x18\x02 \x01(\x05\x12\x0f\n\x07save_id\x18\x03 \x01(\x05\x12\x15\n\rsave_to_robot\x18\x04 \x01(\x08\x12\x10\n\x08say_name\x18\x05 \x01(\x08\x12\x11\n\tuse_music\x18\x06 \x01(\x08\"Y\n\x17SetFaceToEnrollResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x18\n\x16\x44riveOffChargerRequest\"\x9a\x01\n\x17\x44riveOffChargerResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12?\n\x06result\x18\x02 \x01(\x0e\x32/.Anki.Vector.external_interface.BehaviorResults\"\x17\n\x15\x44riveOnChargerRequest\"\x99\x01\n\x16\x44riveOnChargerResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12?\n\x06result\x18\x02 \x01(\x0e\x32/.Anki.Vector.external_interface.BehaviorResults\"\x0f\n\rWakeWordBegin\"8\n\x0bWakeWordEnd\x12\x14\n\x0cintent_heard\x18\x01 \x01(\x08\x12\x13\n\x0bintent_json\x18\x02 \x01(\t\"n\n\tPhotoInfo\x12\x10\n\x08photo_id\x18\x01 \x01(\r\x12\x15\n\rtimestamp_utc\x18\x02 \x01(\r\x12\x1b\n\x13photo_copied_to_app\x18\x03 \x01(\x08\x12\x1b\n\x13thumb_copied_to_app\x18\x04 \x01(\x08\"\x13\n\x11PhotosInfoRequest\"\x94\x01\n\x12PhotosInfoResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12>\n\x0bphoto_infos\x18\x02 \x03(\x0b\x32).Anki.Vector.external_interface.PhotoInfo\" \n\x0cPhotoRequest\x12\x10\n\x08photo_id\x18\x01 \x01(\r\"6\n\x10PhotoPathMessage\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x11\n\tfull_path\x18\x02 \x01(\t\"o\n\rPhotoResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\r\n\x05image\x18\x03 \x01(\x0c\"$\n\x10ThumbnailRequest\x12\x10\n\x08photo_id\x18\x01 \x01(\r\":\n\x14ThumbnailPathMessage\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x11\n\tfull_path\x18\x02 \x01(\t\"s\n\x11ThumbnailResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\r\n\x05image\x18\x03 \x01(\x0c\"&\n\x12\x44\x65letePhotoRequest\x12\x10\n\x08photo_id\x18\x01 \x01(\r\"f\n\x13\x44\x65letePhotoResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x0f\n\x07success\x18\x02 \x01(\x08\"\x1e\n\nPhotoTaken\x12\x10\n\x08photo_id\x18\x01 \x01(\r\"\xc1\x02\n\x11PathMotionProfile\x12\x12\n\nspeed_mmps\x18\x01 \x01(\x02\x12\x13\n\x0b\x61\x63\x63\x65l_mmps2\x18\x02 \x01(\x02\x12\x13\n\x0b\x64\x65\x63\x65l_mmps2\x18\x03 \x01(\x02\x12$\n\x1cpoint_turn_speed_rad_per_sec\x18\x04 \x01(\x02\x12%\n\x1dpoint_turn_accel_rad_per_sec2\x18\x05 \x01(\x02\x12%\n\x1dpoint_turn_decel_rad_per_sec2\x18\x06 \x01(\x02\x12\x17\n\x0f\x64ock_speed_mmps\x18\x07 \x01(\x02\x12\x18\n\x10\x64ock_accel_mmps2\x18\x08 \x01(\x02\x12\x18\n\x10\x64ock_decel_mmps2\x18\t \x01(\x02\x12\x1a\n\x12reverse_speed_mmps\x18\n \x01(\x02\x12\x11\n\tis_custom\x18\x0b \x01(\x08\"\xf8\x0c\n\x0c\x41\x63tionResult\x12K\n\x04\x63ode\x18\x01 \x01(\x0e\x32=.Anki.Vector.external_interface.ActionResult.ActionResultCode\"\x9a\x0c\n\x10\x41\x63tionResultCode\x12\x19\n\x15\x41\x43TION_RESULT_SUCCESS\x10\x00\x12\x1c\n\x15\x41\x43TION_RESULT_RUNNING\x10\x80\x80\x80\x08\x12,\n%ACTION_RESULT_CANCELLED_WHILE_RUNNING\x10\x80\x80\x80\x10\x12\x12\n\x0bNOT_STARTED\x10\x81\x80\x80\x10\x12\x0c\n\x05\x41\x42ORT\x10\x80\x80\x80\x18\x12\x13\n\x0c\x41NIM_ABORTED\x10\x81\x80\x80\x18\x12\x11\n\nBAD_MARKER\x10\x82\x80\x80\x18\x12\x16\n\x0f\x42\x41\x44_MESSAGE_TAG\x10\x83\x80\x80\x18\x12\x11\n\nBAD_OBJECT\x10\x84\x80\x80\x18\x12\x0f\n\x08\x42\x41\x44_POSE\x10\x85\x80\x80\x18\x12\x0e\n\x07\x42\x41\x44_TAG\x10\x86\x80\x80\x18\x12\x1e\n\x17\x43HARGER_UNPLUGGED_ABORT\x10\x87\x80\x80\x18\x12!\n\x1a\x43LIFF_ALIGN_FAILED_TIMEOUT\x10\x88\x80\x80\x18\x12$\n\x1d\x43LIFF_ALIGN_FAILED_NO_TURNING\x10\x89\x80\x80\x18\x12&\n\x1f\x43LIFF_ALIGN_FAILED_OVER_TURNING\x10\x8a\x80\x80\x18\x12\"\n\x1b\x43LIFF_ALIGN_FAILED_NO_WHITE\x10\x8b\x80\x80\x18\x12!\n\x1a\x43LIFF_ALIGN_FAILED_STOPPED\x10\x8c\x80\x80\x18\x12!\n\x1a\x46\x41ILED_SETTING_CALIBRATION\x10\x8d\x80\x80\x18\x12(\n!FOLLOWING_PATH_BUT_NOT_TRAVERSING\x10\x8e\x80\x80\x18\x12\x12\n\x0bINTERRUPTED\x10\x8f\x80\x80\x18\x12\x1f\n\x18INVALID_OFF_TREADS_STATE\x10\x90\x80\x80\x18\x12\x19\n\x12MISMATCHED_UP_AXIS\x10\x91\x80\x80\x18\x12\x13\n\x0cNO_ANIM_NAME\x10\x92\x80\x80\x18\x12\x16\n\x0fNO_DISTANCE_SET\x10\x93\x80\x80\x18\x12\x0e\n\x07NO_FACE\x10\x94\x80\x80\x18\x12\x12\n\x0bNO_GOAL_SET\x10\x95\x80\x80\x18\x12\x19\n\x12NO_PREACTION_POSES\x10\x96\x80\x80\x18\x12 \n\x19NOT_CARRYING_OBJECT_ABORT\x10\x97\x80\x80\x18\x12\x1b\n\x14NOT_ON_CHARGER_ABORT\x10\x98\x80\x80\x18\x12\x15\n\x0eNULL_SUBACTION\x10\x99\x80\x80\x18\x12!\n\x1aPATH_PLANNING_FAILED_ABORT\x10\x9a\x80\x80\x18\x12(\n!PICKUP_OBJECT_UNEXPECTEDLY_MOVING\x10\x9b\x80\x80\x18\x12#\n\x1cSEND_MESSAGE_TO_ROBOT_FAILED\x10\x9c\x80\x80\x18\x12\x1c\n\x15STILL_CARRYING_OBJECT\x10\x9d\x80\x80\x18\x12\x0e\n\x07TIMEOUT\x10\x9e\x80\x80\x18\x12\x14\n\rTRACKS_LOCKED\x10\x9f\x80\x80\x18\x12\x1d\n\x16UNEXPECTED_DOCK_ACTION\x10\xa0\x80\x80\x18\x12\x18\n\x11UNKNOWN_TOOL_CODE\x10\xa1\x80\x80\x18\x12\x1c\n\x15UPDATE_DERIVED_FAILED\x10\xa2\x80\x80\x18\x12 \n\x19VISUAL_OBSERVATION_FAILED\x10\xa3\x80\x80\x18\x12 \n\x19SHOULDNT_DRIVE_ON_CHARGER\x10\xa4\x80\x80\x18\x12\x0c\n\x05RETRY\x10\x80\x80\x80 \x12#\n\x1c\x44ID_NOT_REACH_PREACTION_POSE\x10\x81\x80\x80 \x12\x1d\n\x16\x46\x41ILED_TRAVERSING_PATH\x10\x82\x80\x80 \x12!\n\x1aLAST_PICK_AND_PLACE_FAILED\x10\x83\x80\x80 \x12$\n\x1dMOTOR_STOPPED_MAKING_PROGRESS\x10\x84\x80\x80 \x12 \n\x19NOT_CARRYING_OBJECT_RETRY\x10\x85\x80\x80 \x12\x1b\n\x14NOT_ON_CHARGER_RETRY\x10\x86\x80\x80 \x12!\n\x1aPATH_PLANNING_FAILED_RETRY\x10\x87\x80\x80 \x12\x1e\n\x17PLACEMENT_GOAL_NOT_FREE\x10\x88\x80\x80 \x12,\n%PICKUP_OBJECT_UNEXPECTEDLY_NOT_MOVING\x10\x89\x80\x80 \x12\x17\n\x10STILL_ON_CHARGER\x10\x8a\x80\x80 \x12\x1d\n\x16UNEXPECTED_PITCH_ANGLE\x10\x8b\x80\x80 \"\xa7\x01\n\x0fGoToPoseRequest\x12\x0c\n\x04x_mm\x18\x01 \x01(\x02\x12\x0c\n\x04y_mm\x18\x02 \x01(\x02\x12\x0b\n\x03rad\x18\x03 \x01(\x02\x12\x46\n\x0bmotion_prof\x18\x04 \x01(\x0b\x32\x31.Anki.Vector.external_interface.PathMotionProfile\x12\x0e\n\x06id_tag\x18\x05 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x06 \x01(\x05\"\x90\x01\n\x10GoToPoseResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\xd0\x02\n\x13\x44ockWithCubeRequest\x12\x11\n\tobject_id\x18\x01 \x01(\x05\x12\x1f\n\x17\x64istance_from_marker_mm\x18\x02 \x01(\x02\x12\x1a\n\x12\x61pproach_angle_rad\x18\x03 \x01(\x02\x12\x45\n\x0e\x61lignment_type\x18\x04 \x01(\x0e\x32-.Anki.Vector.external_interface.AlignmentType\x12\x1a\n\x12use_approach_angle\x18\x05 \x01(\x08\x12\x19\n\x11use_pre_dock_pose\x18\x06 \x01(\x08\x12\x46\n\x0bmotion_prof\x18\x07 \x01(\x0b\x32\x31.Anki.Vector.external_interface.PathMotionProfile\x12\x0e\n\x06id_tag\x18\x08 \x01(\x05\x12\x13\n\x0bnum_retries\x18\t \x01(\x05\"\x94\x01\n\x14\x44ockWithCubeResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\x7f\n\x14\x44riveStraightRequest\x12\x12\n\nspeed_mmps\x18\x01 \x01(\x02\x12\x0f\n\x07\x64ist_mm\x18\x02 \x01(\x02\x12\x1d\n\x15should_play_animation\x18\x03 \x01(\x08\x12\x0e\n\x06id_tag\x18\x04 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x05 \x01(\x05\"\x95\x01\n\x15\x44riveStraightResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\xa9\x01\n\x12TurnInPlaceRequest\x12\x11\n\tangle_rad\x18\x01 \x01(\x02\x12\x19\n\x11speed_rad_per_sec\x18\x02 \x01(\x02\x12\x1a\n\x12\x61\x63\x63\x65l_rad_per_sec2\x18\x03 \x01(\x02\x12\x0f\n\x07tol_rad\x18\x04 \x01(\x02\x12\x13\n\x0bis_absolute\x18\x05 \x01(\r\x12\x0e\n\x06id_tag\x18\x06 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x07 \x01(\x05\"\x93\x01\n\x13TurnInPlaceResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\x9e\x01\n\x13SetHeadAngleRequest\x12\x11\n\tangle_rad\x18\x01 \x01(\x02\x12\x1d\n\x15max_speed_rad_per_sec\x18\x02 \x01(\x02\x12\x1a\n\x12\x61\x63\x63\x65l_rad_per_sec2\x18\x03 \x01(\x02\x12\x14\n\x0c\x64uration_sec\x18\x04 \x01(\x02\x12\x0e\n\x06id_tag\x18\x05 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x06 \x01(\x05\"\x94\x01\n\x14SetHeadAngleResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\x9f\x01\n\x14SetLiftHeightRequest\x12\x11\n\theight_mm\x18\x01 \x01(\x02\x12\x1d\n\x15max_speed_rad_per_sec\x18\x02 \x01(\x02\x12\x1a\n\x12\x61\x63\x63\x65l_rad_per_sec2\x18\x03 \x01(\x02\x12\x14\n\x0c\x64uration_sec\x18\x04 \x01(\x02\x12\x0e\n\x06id_tag\x18\x05 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x06 \x01(\x05\"\x95\x01\n\x15SetLiftHeightResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\xb9\x03\n\x0cNetworkStats\x12#\n\x1bg_net_stat1_num_connections\x18\x01 \x01(\x05\x12\x1f\n\x17g_net_stat2_latency_avg\x18\x02 \x01(\x02\x12\x1e\n\x16g_net_stat3_latency_sd\x18\x03 \x01(\x02\x12\x1f\n\x17g_net_stat4_latency_min\x18\x04 \x01(\x02\x12\x1f\n\x17g_net_stat5_latency_max\x18\x05 \x01(\x02\x12#\n\x1bg_net_stat6_ping_arrived_pc\x18\x06 \x01(\x02\x12%\n\x1dg_net_stat7_ext_queued_avg_ms\x18\x07 \x01(\x02\x12%\n\x1dg_net_stat8_ext_queued_min_ms\x18\x08 \x01(\x02\x12%\n\x1dg_net_stat9_ext_queued_max_ms\x18\t \x01(\x02\x12!\n\x19g_net_stata_queued_avg_ms\x18\n \x01(\x02\x12!\n\x19g_net_statb_queued_min_ms\x18\x0b \x01(\x02\x12!\n\x19g_net_statc_queued_max_ms\x18\x0c \x01(\x02\"\x15\n\x13\x42\x61tteryStateRequest\"\xc9\x02\n\x14\x42\x61tteryStateResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x43\n\rbattery_level\x18\x02 \x01(\x0e\x32,.Anki.Vector.external_interface.BatteryLevel\x12\x15\n\rbattery_volts\x18\x03 \x01(\x02\x12\x13\n\x0bis_charging\x18\x04 \x01(\x08\x12\x1e\n\x16is_on_charger_platform\x18\x05 \x01(\x08\x12\x1d\n\x15suggested_charger_sec\x18\x06 \x01(\x02\x12\x41\n\x0c\x63ube_battery\x18\x07 \x01(\x0b\x32+.Anki.Vector.external_interface.CubeBattery\"\xd3\x01\n\x0b\x43ubeBattery\x12K\n\x05level\x18\x01 \x01(\x0e\x32<.Anki.Vector.external_interface.CubeBattery.CubeBatteryLevel\x12\x12\n\nfactory_id\x18\x02 \x01(\t\x12\x15\n\rbattery_volts\x18\x03 \x01(\x02\x12#\n\x1btime_since_last_reading_sec\x18\x04 \x01(\x02\"\'\n\x10\x43ubeBatteryLevel\x12\x07\n\x03Low\x10\x00\x12\n\n\x06Normal\x10\x01\"\x15\n\x13VersionStateRequest\"\x83\x01\n\x14VersionStateResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x12\n\nos_version\x18\x02 \x01(\t\x12\x17\n\x0f\x65ngine_build_id\x18\x03 \x01(\t\"\x15\n\x13NetworkStateRequest\"\x9b\x01\n\x14NetworkStateResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x43\n\rnetwork_stats\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.NetworkStats\"Q\n\x0eSayTextRequest\x12\x0c\n\x04text\x18\x01 \x01(\t\x12\x18\n\x10use_vector_voice\x18\x02 \x01(\x08\x12\x17\n\x0f\x64uration_scalar\x18\x03 \x01(\x02\"\xf5\x01\n\x0fSayTextResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12M\n\x05state\x18\x02 \x01(\x0e\x32>.Anki.Vector.external_interface.SayTextResponse.UtteranceState\"S\n\x0eUtteranceState\x12\x0b\n\x07INVALID\x10\x00\x12\x0e\n\nGENERATING\x10\x01\x12\t\n\x05READY\x10\x02\x12\x0b\n\x07PLAYING\x10\x03\x12\x0c\n\x08\x46INISHED\x10\x04\"\x9b\x01\n\x0fStimulationInfo\x12\x16\n\x0e\x65motion_events\x18\x01 \x03(\t\x12\r\n\x05value\x18\x02 \x01(\x02\x12\x10\n\x08velocity\x18\x03 \x01(\x02\x12\r\n\x05\x61\x63\x63\x65l\x18\x04 \x01(\x02\x12\x1a\n\x12value_before_event\x18\x05 \x01(\x02\x12\x11\n\tmin_value\x18\x06 \x01(\x02\x12\x11\n\tmax_value\x18\x07 \x01(\x02\"Y\n\x14\x41udioSendModeRequest\x12\x41\n\x04mode\x18\x01 \x01(\x0e\x32\x33.Anki.Vector.external_interface.AudioProcessingMode\"Y\n\x14\x41udioSendModeChanged\x12\x41\n\x04mode\x18\x01 \x01(\x0e\x32\x33.Anki.Vector.external_interface.AudioProcessingMode\"\xe8\x01\n\nAudioChunk\x12\x18\n\x10robot_time_stamp\x18\x01 \x01(\r\x12\x10\n\x08group_id\x18\x02 \x01(\r\x12\x10\n\x08\x63hunk_id\x18\x03 \x01(\r\x12\x19\n\x11\x61udio_chunk_count\x18\x04 \x01(\r\x12\x14\n\x0csignal_power\x18\x05 \x01(\x0c\x12\x1b\n\x13\x64irection_strengths\x18\x06 \x01(\x0c\x12\x18\n\x10source_direction\x18\x07 \x01(\r\x12\x19\n\x11source_confidence\x18\x08 \x01(\r\x12\x19\n\x11noise_floor_power\x18\t \x01(\r\"\x12\n\x10\x41udioFeedRequest\"\xc8\x01\n\x11\x41udioFeedResponse\x12\x18\n\x10robot_time_stamp\x18\x01 \x01(\r\x12\x10\n\x08group_id\x18\x02 \x01(\r\x12\x14\n\x0csignal_power\x18\x03 \x01(\x0c\x12\x1b\n\x13\x64irection_strengths\x18\x04 \x01(\x0c\x12\x18\n\x10source_direction\x18\x05 \x01(\r\x12\x19\n\x11source_confidence\x18\x06 \x01(\r\x12\x19\n\x11noise_floor_power\x18\x07 \x01(\r:\x04\x80\xa6\x1d\x01\".\n\x1c\x45nableMarkerDetectionRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"_\n\x1d\x45nableMarkerDetectionResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\xb1\x01\n\x1a\x45nableFaceDetectionRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\x12\x1e\n\x16\x65nable_smile_detection\x18\x02 \x01(\x08\x12$\n\x1c\x65nable_expression_estimation\x18\x03 \x01(\x08\x12\x1e\n\x16\x65nable_blink_detection\x18\x04 \x01(\x08\x12\x1d\n\x15\x65nable_gaze_detection\x18\x05 \x01(\x08\"]\n\x1b\x45nableFaceDetectionResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\".\n\x1c\x45nableMotionDetectionRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"_\n\x1d\x45nableMotionDetectionResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\")\n\x17\x45nableMirrorModeRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"Z\n\x18\x45nableMirrorModeResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x14\n\x12MirrorModeDisabled\"-\n\x1b\x45nableImageStreamingRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"^\n\x1c\x45nableImageStreamingResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x19\n\x17VisionModesAutoDisabled\"\xd1\x03\n\nImageChunk\x12\x18\n\x10\x66rame_time_stamp\x18\x01 \x01(\r\x12\x10\n\x08image_id\x18\x02 \x01(\r\x12\r\n\x05width\x18\x03 \x01(\r\x12\x0e\n\x06height\x18\x04 \x01(\r\x12P\n\x0eimage_encoding\x18\x05 \x01(\x0e\x32\x38.Anki.Vector.external_interface.ImageChunk.ImageEncoding\x12\x15\n\rdisplay_index\x18\x06 \x01(\r\x12\x19\n\x11image_chunk_count\x18\x07 \x01(\r\x12\x10\n\x08\x63hunk_id\x18\x08 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\t \x01(\x0c\"\xd3\x01\n\rImageEncoding\x12\x17\n\x13NONE_IMAGE_ENCODING\x10\x00\x12\x0c\n\x08RAW_GRAY\x10\x01\x12\x0b\n\x07RAW_RGB\x10\x02\x12\x08\n\x04YUYV\x10\x03\x12\x0c\n\x08YUV420SP\x10\x04\x12\t\n\x05\x42\x41YER\x10\x05\x12\r\n\tJPEG_GRAY\x10\x06\x12\x0e\n\nJPEG_COLOR\x10\x07\x12\x19\n\x15JPEG_COLOR_HALF_WIDTH\x10\x08\x12\x17\n\x13JPEG_MINIMIZED_GRAY\x10\t\x12\x18\n\x14JPEG_MINIMIZED_COLOR\x10\n\"\x13\n\x11\x43\x61meraFeedRequest\"\xa6\x01\n\x12\x43\x61meraFeedResponse\x12\x18\n\x10\x66rame_time_stamp\x18\x01 \x01(\r\x12\x10\n\x08image_id\x18\x02 \x01(\r\x12P\n\x0eimage_encoding\x18\x03 \x01(\x0e\x32\x38.Anki.Vector.external_interface.ImageChunk.ImageEncoding\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c:\x04\x80\xa6\x1d\x01\"5\n\x12SetEyeColorRequest\x12\x0b\n\x03hue\x18\x01 \x01(\x02\x12\x12\n\nsaturation\x18\x02 \x01(\x02\"U\n\x13SetEyeColorResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x96\x01\n\x18SDKInitializationRequest\x12\x1a\n\x12sdk_module_version\x18\x01 \x01(\t\x12\x16\n\x0epython_version\x18\x02 \x01(\t\x12\x1d\n\x15python_implementation\x18\x03 \x01(\t\x12\x12\n\nos_version\x18\x04 \x01(\t\x12\x13\n\x0b\x63pu_version\x18\x05 \x01(\t\"[\n\x19SDKInitializationResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus*\xbd\x01\n\x10\x46\x61\x63ialExpression\x12\x16\n\x12\x45XPRESSION_UNKNOWN\x10\x00\x12\x16\n\x12\x45XPRESSION_NEUTRAL\x10\x01\x12\x18\n\x14\x45XPRESSION_HAPPINESS\x10\x02\x12\x17\n\x13\x45XPRESSION_SURPRISE\x10\x03\x12\x14\n\x10\x45XPRESSION_ANGER\x10\x04\x12\x16\n\x12\x45XPRESSION_SADNESS\x10\x05\x12\x14\n\x10\x45XPRESSION_COUNT\x10\x05\x1a\x02\x10\x01*\xcc\x01\n\x14\x46\x61\x63\x65\x45nrollmentResult\x12\x0b\n\x07SUCCESS\x10\x00\x12\x12\n\x0eSAW_WRONG_FACE\x10\x01\x12\x16\n\x12SAW_MULTIPLE_FACES\x10\x02\x12\r\n\tTIMED_OUT\x10\x03\x12\x0f\n\x0bSAVE_FAILED\x10\x04\x12\x0e\n\nINCOMPLETE\x10\x05\x12\r\n\tCANCELLED\x10\x06\x12\x0f\n\x0bNAME_IN_USE\x10\x07\x12\x16\n\x12NAMED_STORAGE_FULL\x10\x08\x12\x13\n\x0fUNKNOWN_FAILURE\x10\t*l\n\x0f\x42\x65haviorResults\x12\x1a\n\x16\x42\x45HAVIOR_INVALID_STATE\x10\x00\x12\x1b\n\x17\x42\x45HAVIOR_COMPLETE_STATE\x10\x01\x12 \n\x1c\x42\x45HAVIOR_WONT_ACTIVATE_STATE\x10\x02*S\n\x12\x41\x63tionTagConstants\x12\x13\n\x0fINVALID_SDK_TAG\x10\x00\x12\x13\n\rFIRST_SDK_TAG\x10\x81\x89z\x12\x13\n\x0cLAST_SDK_TAG\x10\xc0\x8d\xb7\x01*\x9e\x01\n\rAlignmentType\x12\x1a\n\x16\x41LIGNMENT_TYPE_UNKNOWN\x10\x00\x12\x1e\n\x1a\x41LIGNMENT_TYPE_LIFT_FINGER\x10\x01\x12\x1d\n\x19\x41LIGNMENT_TYPE_LIFT_PLATE\x10\x02\x12\x17\n\x13\x41LIGNMENT_TYPE_BODY\x10\x03\x12\x19\n\x15\x41LIGNMENT_TYPE_CUSTOM\x10\x04*s\n\x0c\x42\x61tteryLevel\x12\x19\n\x15\x42\x41TTERY_LEVEL_UNKNOWN\x10\x00\x12\x15\n\x11\x42\x41TTERY_LEVEL_LOW\x10\x01\x12\x19\n\x15\x42\x41TTERY_LEVEL_NOMINAL\x10\x02\x12\x16\n\x12\x42\x41TTERY_LEVEL_FULL\x10\x03*\xcc\x01\n\x0e\x41udioConstants\x12\x18\n\x14\x41UDIO_CONSTANTS_NULL\x10\x00\x12\x1c\n\x18MIC_DETECTION_DIRECTIONS\x10\x0c\x12%\n SAMPLE_COUNTS_PER_ENGINE_MESSAGE\x10\xa0\x01\x12\"\n\x1dSAMPLE_COUNTS_PER_SDK_MESSAGE\x10\xc0\x0c\x12\x1b\n\x16MICROPHONE_SAMPLE_RATE\x10\x89z\x12\x1a\n\x15PROCESSED_SAMPLE_RATE\x10\x80}*\x85\x01\n\x13\x41udioProcessingMode\x12\x11\n\rAUDIO_UNKNOWN\x10\x00\x12\r\n\tAUDIO_OFF\x10\x01\x12\x13\n\x0f\x41UDIO_FAST_MODE\x10\x02\x12\x1a\n\x16\x41UDIO_DIRECTIONAL_MODE\x10\x03\x12\x1b\n\x17\x41UDIO_VOICE_DETECT_MODE\x10\x04\x62\x06proto3') + serialized_pb=_b('\n$anki_vector/messaging/messages.proto\x12\x1e\x41nki.Vector.external_interface\x1a+anki_vector/messaging/response_status.proto\x1a&anki_vector/messaging/extensions.proto\"\x0f\n\rKeepAlivePing\"\x19\n\tAnimation\x12\x0c\n\x04name\x18\x01 \x01(\t\"|\n\x12\x44riveWheelsRequest\x12\x17\n\x0fleft_wheel_mmps\x18\x01 \x01(\x02\x12\x18\n\x10right_wheel_mmps\x18\x02 \x01(\x02\x12\x18\n\x10left_wheel_mmps2\x18\x03 \x01(\x02\x12\x19\n\x11right_wheel_mmps2\x18\x04 \x01(\x02\"U\n\x13\x44riveWheelsResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\",\n\x0fMoveHeadRequest\x12\x19\n\x11speed_rad_per_sec\x18\x01 \x01(\x02\"R\n\x10MoveHeadResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\",\n\x0fMoveLiftRequest\x12\x19\n\x11speed_rad_per_sec\x18\x01 \x01(\x02\"R\n\x10MoveLiftResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\xb4\x01\n\x14PlayAnimationRequest\x12<\n\tanimation\x18\x01 \x01(\x0b\x32).Anki.Vector.external_interface.Animation\x12\r\n\x05loops\x18\x02 \x01(\r\x12\x19\n\x11ignore_body_track\x18\x03 \x01(\x08\x12\x19\n\x11ignore_head_track\x18\x04 \x01(\x08\x12\x19\n\x11ignore_lift_track\x18\x05 \x01(\x08\"\x98\x01\n\x15PlayAnimationResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12?\n\x06result\x18\x02 \x01(\x0e\x32/.Anki.Vector.external_interface.BehaviorResults\"\x17\n\x15ListAnimationsRequest\"\x9c\x01\n\x16ListAnimationsResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x42\n\x0f\x61nimation_names\x18\x02 \x03(\x0b\x32).Anki.Vector.external_interface.Animation\"_\n\x1a\x44isplayFaceImageRGBRequest\x12\x11\n\tface_data\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64uration_ms\x18\x02 \x01(\r\x12\x19\n\x11interrupt_running\x18\x03 \x01(\x08\"]\n\x1b\x44isplayFaceImageRGBResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x1b\n\x19MeetVictorFaceScanStarted\"\x1c\n\x1aMeetVictorFaceScanComplete\"\xbf\x02\n\x06Status\x12\x62\n\x1dmeet_victor_face_scan_started\x18\x02 \x01(\x0b\x32\x39.Anki.Vector.external_interface.MeetVictorFaceScanStartedH\x00\x12\x64\n\x1emeet_victor_face_scan_complete\x18\x03 \x01(\x0b\x32:.Anki.Vector.external_interface.MeetVictorFaceScanCompleteH\x00\x12\\\n\x19\x66\x61\x63\x65_enrollment_completed\x18\x04 \x01(\x0b\x32\x37.Anki.Vector.external_interface.FaceEnrollmentCompletedH\x00\x42\r\n\x0bstatus_type\"\xac\x01\n\x08WakeWord\x12H\n\x0fwake_word_begin\x18\x01 \x01(\x0b\x32-.Anki.Vector.external_interface.WakeWordBeginH\x00\x12\x44\n\rwake_word_end\x18\x02 \x01(\x0b\x32+.Anki.Vector.external_interface.WakeWordEndH\x00\x42\x10\n\x0ewake_word_type\"b\n\x11TimeStampedStatus\x12\x36\n\x06status\x18\x01 \x01(\x0b\x32&.Anki.Vector.external_interface.Status\x12\x15\n\rtimestamp_utc\x18\x02 \x01(\r\"p\n\nPoseStruct\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\x12\n\n\x02q0\x18\x04 \x01(\x02\x12\n\n\x02q1\x18\x05 \x01(\x02\x12\n\n\x02q2\x18\x06 \x01(\x02\x12\n\n\x02q3\x18\x07 \x01(\x02\x12\x11\n\torigin_id\x18\x08 \x01(\r\",\n\tAccelData\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\"+\n\x08GyroData\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\"\xa3\x01\n\x08ProxData\x12\x13\n\x0b\x64istance_mm\x18\x01 \x01(\r\x12\x16\n\x0esignal_quality\x18\x02 \x01(\x02\x12\x19\n\x11is_in_valid_range\x18\x03 \x01(\x08\x12\x1f\n\x17is_valid_signal_quality\x18\x04 \x01(\x08\x12\x16\n\x0eis_lift_in_fov\x18\x05 \x01(\x08\x12\x16\n\x0eis_too_pitched\x18\x06 \x01(\x08\">\n\tTouchData\x12\x17\n\x0fraw_touch_value\x18\x01 \x01(\r\x12\x18\n\x10is_being_touched\x18\x02 \x01(\x08\"\x82\x05\n\nRobotState\x12\x38\n\x04pose\x18\x01 \x01(\x0b\x32*.Anki.Vector.external_interface.PoseStruct\x12\x16\n\x0epose_angle_rad\x18\x02 \x01(\x02\x12\x16\n\x0epose_pitch_rad\x18\x03 \x01(\x02\x12\x1d\n\x15left_wheel_speed_mmps\x18\x04 \x01(\x02\x12\x1e\n\x16right_wheel_speed_mmps\x18\x05 \x01(\x02\x12\x16\n\x0ehead_angle_rad\x18\x06 \x01(\x02\x12\x16\n\x0elift_height_mm\x18\x07 \x01(\x02\x12\x38\n\x05\x61\x63\x63\x65l\x18\x08 \x01(\x0b\x32).Anki.Vector.external_interface.AccelData\x12\x36\n\x04gyro\x18\t \x01(\x0b\x32(.Anki.Vector.external_interface.GyroData\x12\x1a\n\x12\x63\x61rrying_object_id\x18\n \x01(\x05\x12!\n\x19\x63\x61rrying_object_on_top_id\x18\x0b \x01(\x05\x12\x1f\n\x17head_tracking_object_id\x18\x0c \x01(\x05\x12\x1e\n\x16localized_to_object_id\x18\r \x01(\x05\x12\x1d\n\x15last_image_time_stamp\x18\x0e \x01(\r\x12\x0e\n\x06status\x18\x0f \x01(\r\x12;\n\tprox_data\x18\x10 \x01(\x0b\x32(.Anki.Vector.external_interface.ProxData\x12=\n\ntouch_data\x18\x11 \x01(\x0b\x32).Anki.Vector.external_interface.TouchData\"!\n\tCladPoint\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\"Q\n\x08\x43ladRect\x12\x12\n\nx_top_left\x18\x01 \x01(\x02\x12\x12\n\ny_top_left\x18\x02 \x01(\x02\x12\r\n\x05width\x18\x03 \x01(\x02\x12\x0e\n\x06height\x18\x04 \x01(\x02\"\x8a\x04\n\x11RobotObservedFace\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x01 \x01(\x05\x12\x11\n\ttimestamp\x18\x02 \x01(\r\x12\x38\n\x04pose\x18\x03 \x01(\x0b\x32*.Anki.Vector.external_interface.PoseStruct\x12:\n\x08img_rect\x18\x04 \x01(\x0b\x32(.Anki.Vector.external_interface.CladRect\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x44\n\nexpression\x18\x06 \x01(\x0e\x32\x30.Anki.Vector.external_interface.FacialExpression\x12\x19\n\x11\x65xpression_values\x18\x07 \x03(\r\x12;\n\x08left_eye\x18\x08 \x03(\x0b\x32).Anki.Vector.external_interface.CladPoint\x12<\n\tright_eye\x18\t \x03(\x0b\x32).Anki.Vector.external_interface.CladPoint\x12\x37\n\x04nose\x18\n \x03(\x0b\x32).Anki.Vector.external_interface.CladPoint\x12\x38\n\x05mouth\x18\x0b \x03(\x0b\x32).Anki.Vector.external_interface.CladPoint\"<\n\x1aRobotChangedObservedFaceID\x12\x0e\n\x06old_id\x18\x01 \x01(\x05\x12\x0e\n\x06new_id\x18\x02 \x01(\x05\"1\n\x10\x41ppIntentRequest\x12\x0e\n\x06intent\x18\x01 \x01(\t\x12\r\n\x05param\x18\x02 \x01(\t\"\x11\n\x0f\x41ppDisconnected\"S\n\x11\x41ppIntentResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"~\n\x17\x46\x61\x63\x65\x45nrollmentCompleted\x12\x44\n\x06result\x18\x01 \x01(\x0e\x32\x34.Anki.Vector.external_interface.FaceEnrollmentResult\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x02 \x01(\x05\x12\x0c\n\x04name\x18\x03 \x01(\t\"\x1d\n\x1b\x43\x61ncelFaceEnrollmentRequest\"^\n\x1c\x43\x61ncelFaceEnrollmentResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x1d\n\x1bRequestEnrolledNamesRequest\"\xc2\x01\n\x0fLoadedKnownFace\x12$\n\x1cseconds_since_first_enrolled\x18\x01 \x01(\x03\x12\"\n\x1aseconds_since_last_updated\x18\x02 \x01(\x03\x12\x1f\n\x17seconds_since_last_seen\x18\x03 \x01(\x03\x12%\n\x1dlast_seen_seconds_since_epoch\x18\x04 \x01(\x03\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x05 \x01(\x05\x12\x0c\n\x04name\x18\x06 \x01(\t\"9\n\x18RobotRenamedEnrolledFace\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x01 \x01(\x05\x12\x0c\n\x04name\x18\x02 \x01(\t\"\x9e\x01\n\x1cRequestEnrolledNamesResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12>\n\x05\x66\x61\x63\x65s\x18\x02 \x03(\x0b\x32/.Anki.Vector.external_interface.LoadedKnownFace\"T\n\x1dUpdateEnrolledFaceByIDRequest\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x01 \x01(\x05\x12\x10\n\x08old_name\x18\x02 \x01(\t\x12\x10\n\x08new_name\x18\x03 \x01(\t\"`\n\x1eUpdateEnrolledFaceByIDResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"/\n\x1c\x45raseEnrolledFaceByIDRequest\x12\x0f\n\x07\x66\x61\x63\x65_id\x18\x01 \x01(\x05\"_\n\x1d\x45raseEnrolledFaceByIDResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x1e\n\x1c\x45raseAllEnrolledFacesRequest\"_\n\x1d\x45raseAllEnrolledFacesResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x88\x01\n\x16SetFaceToEnrollRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0bobserved_id\x18\x02 \x01(\x05\x12\x0f\n\x07save_id\x18\x03 \x01(\x05\x12\x15\n\rsave_to_robot\x18\x04 \x01(\x08\x12\x10\n\x08say_name\x18\x05 \x01(\x08\x12\x11\n\tuse_music\x18\x06 \x01(\x08\"Y\n\x17SetFaceToEnrollResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x18\n\x16\x44riveOffChargerRequest\"\x9a\x01\n\x17\x44riveOffChargerResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12?\n\x06result\x18\x02 \x01(\x0e\x32/.Anki.Vector.external_interface.BehaviorResults\"\x17\n\x15\x44riveOnChargerRequest\"\x99\x01\n\x16\x44riveOnChargerResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12?\n\x06result\x18\x02 \x01(\x0e\x32/.Anki.Vector.external_interface.BehaviorResults\"\x0f\n\rWakeWordBegin\"8\n\x0bWakeWordEnd\x12\x14\n\x0cintent_heard\x18\x01 \x01(\x08\x12\x13\n\x0bintent_json\x18\x02 \x01(\t\"n\n\tPhotoInfo\x12\x10\n\x08photo_id\x18\x01 \x01(\r\x12\x15\n\rtimestamp_utc\x18\x02 \x01(\r\x12\x1b\n\x13photo_copied_to_app\x18\x03 \x01(\x08\x12\x1b\n\x13thumb_copied_to_app\x18\x04 \x01(\x08\"\x13\n\x11PhotosInfoRequest\"\x94\x01\n\x12PhotosInfoResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12>\n\x0bphoto_infos\x18\x02 \x03(\x0b\x32).Anki.Vector.external_interface.PhotoInfo\" \n\x0cPhotoRequest\x12\x10\n\x08photo_id\x18\x01 \x01(\r\"6\n\x10PhotoPathMessage\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x11\n\tfull_path\x18\x02 \x01(\t\"o\n\rPhotoResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\r\n\x05image\x18\x03 \x01(\x0c\"$\n\x10ThumbnailRequest\x12\x10\n\x08photo_id\x18\x01 \x01(\r\":\n\x14ThumbnailPathMessage\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x11\n\tfull_path\x18\x02 \x01(\t\"s\n\x11ThumbnailResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\r\n\x05image\x18\x03 \x01(\x0c\"&\n\x12\x44\x65letePhotoRequest\x12\x10\n\x08photo_id\x18\x01 \x01(\r\"f\n\x13\x44\x65letePhotoResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x0f\n\x07success\x18\x02 \x01(\x08\"\x1e\n\nPhotoTaken\x12\x10\n\x08photo_id\x18\x01 \x01(\r\"\xc1\x02\n\x11PathMotionProfile\x12\x12\n\nspeed_mmps\x18\x01 \x01(\x02\x12\x13\n\x0b\x61\x63\x63\x65l_mmps2\x18\x02 \x01(\x02\x12\x13\n\x0b\x64\x65\x63\x65l_mmps2\x18\x03 \x01(\x02\x12$\n\x1cpoint_turn_speed_rad_per_sec\x18\x04 \x01(\x02\x12%\n\x1dpoint_turn_accel_rad_per_sec2\x18\x05 \x01(\x02\x12%\n\x1dpoint_turn_decel_rad_per_sec2\x18\x06 \x01(\x02\x12\x17\n\x0f\x64ock_speed_mmps\x18\x07 \x01(\x02\x12\x18\n\x10\x64ock_accel_mmps2\x18\x08 \x01(\x02\x12\x18\n\x10\x64ock_decel_mmps2\x18\t \x01(\x02\x12\x1a\n\x12reverse_speed_mmps\x18\n \x01(\x02\x12\x11\n\tis_custom\x18\x0b \x01(\x08\"\xf8\x0c\n\x0c\x41\x63tionResult\x12K\n\x04\x63ode\x18\x01 \x01(\x0e\x32=.Anki.Vector.external_interface.ActionResult.ActionResultCode\"\x9a\x0c\n\x10\x41\x63tionResultCode\x12\x19\n\x15\x41\x43TION_RESULT_SUCCESS\x10\x00\x12\x1c\n\x15\x41\x43TION_RESULT_RUNNING\x10\x80\x80\x80\x08\x12,\n%ACTION_RESULT_CANCELLED_WHILE_RUNNING\x10\x80\x80\x80\x10\x12\x12\n\x0bNOT_STARTED\x10\x81\x80\x80\x10\x12\x0c\n\x05\x41\x42ORT\x10\x80\x80\x80\x18\x12\x13\n\x0c\x41NIM_ABORTED\x10\x81\x80\x80\x18\x12\x11\n\nBAD_MARKER\x10\x82\x80\x80\x18\x12\x16\n\x0f\x42\x41\x44_MESSAGE_TAG\x10\x83\x80\x80\x18\x12\x11\n\nBAD_OBJECT\x10\x84\x80\x80\x18\x12\x0f\n\x08\x42\x41\x44_POSE\x10\x85\x80\x80\x18\x12\x0e\n\x07\x42\x41\x44_TAG\x10\x86\x80\x80\x18\x12\x1e\n\x17\x43HARGER_UNPLUGGED_ABORT\x10\x87\x80\x80\x18\x12!\n\x1a\x43LIFF_ALIGN_FAILED_TIMEOUT\x10\x88\x80\x80\x18\x12$\n\x1d\x43LIFF_ALIGN_FAILED_NO_TURNING\x10\x89\x80\x80\x18\x12&\n\x1f\x43LIFF_ALIGN_FAILED_OVER_TURNING\x10\x8a\x80\x80\x18\x12\"\n\x1b\x43LIFF_ALIGN_FAILED_NO_WHITE\x10\x8b\x80\x80\x18\x12!\n\x1a\x43LIFF_ALIGN_FAILED_STOPPED\x10\x8c\x80\x80\x18\x12!\n\x1a\x46\x41ILED_SETTING_CALIBRATION\x10\x8d\x80\x80\x18\x12(\n!FOLLOWING_PATH_BUT_NOT_TRAVERSING\x10\x8e\x80\x80\x18\x12\x12\n\x0bINTERRUPTED\x10\x8f\x80\x80\x18\x12\x1f\n\x18INVALID_OFF_TREADS_STATE\x10\x90\x80\x80\x18\x12\x19\n\x12MISMATCHED_UP_AXIS\x10\x91\x80\x80\x18\x12\x13\n\x0cNO_ANIM_NAME\x10\x92\x80\x80\x18\x12\x16\n\x0fNO_DISTANCE_SET\x10\x93\x80\x80\x18\x12\x0e\n\x07NO_FACE\x10\x94\x80\x80\x18\x12\x12\n\x0bNO_GOAL_SET\x10\x95\x80\x80\x18\x12\x19\n\x12NO_PREACTION_POSES\x10\x96\x80\x80\x18\x12 \n\x19NOT_CARRYING_OBJECT_ABORT\x10\x97\x80\x80\x18\x12\x1b\n\x14NOT_ON_CHARGER_ABORT\x10\x98\x80\x80\x18\x12\x15\n\x0eNULL_SUBACTION\x10\x99\x80\x80\x18\x12!\n\x1aPATH_PLANNING_FAILED_ABORT\x10\x9a\x80\x80\x18\x12(\n!PICKUP_OBJECT_UNEXPECTEDLY_MOVING\x10\x9b\x80\x80\x18\x12#\n\x1cSEND_MESSAGE_TO_ROBOT_FAILED\x10\x9c\x80\x80\x18\x12\x1c\n\x15STILL_CARRYING_OBJECT\x10\x9d\x80\x80\x18\x12\x0e\n\x07TIMEOUT\x10\x9e\x80\x80\x18\x12\x14\n\rTRACKS_LOCKED\x10\x9f\x80\x80\x18\x12\x1d\n\x16UNEXPECTED_DOCK_ACTION\x10\xa0\x80\x80\x18\x12\x18\n\x11UNKNOWN_TOOL_CODE\x10\xa1\x80\x80\x18\x12\x1c\n\x15UPDATE_DERIVED_FAILED\x10\xa2\x80\x80\x18\x12 \n\x19VISUAL_OBSERVATION_FAILED\x10\xa3\x80\x80\x18\x12 \n\x19SHOULDNT_DRIVE_ON_CHARGER\x10\xa4\x80\x80\x18\x12\x0c\n\x05RETRY\x10\x80\x80\x80 \x12#\n\x1c\x44ID_NOT_REACH_PREACTION_POSE\x10\x81\x80\x80 \x12\x1d\n\x16\x46\x41ILED_TRAVERSING_PATH\x10\x82\x80\x80 \x12!\n\x1aLAST_PICK_AND_PLACE_FAILED\x10\x83\x80\x80 \x12$\n\x1dMOTOR_STOPPED_MAKING_PROGRESS\x10\x84\x80\x80 \x12 \n\x19NOT_CARRYING_OBJECT_RETRY\x10\x85\x80\x80 \x12\x1b\n\x14NOT_ON_CHARGER_RETRY\x10\x86\x80\x80 \x12!\n\x1aPATH_PLANNING_FAILED_RETRY\x10\x87\x80\x80 \x12\x1e\n\x17PLACEMENT_GOAL_NOT_FREE\x10\x88\x80\x80 \x12,\n%PICKUP_OBJECT_UNEXPECTEDLY_NOT_MOVING\x10\x89\x80\x80 \x12\x17\n\x10STILL_ON_CHARGER\x10\x8a\x80\x80 \x12\x1d\n\x16UNEXPECTED_PITCH_ANGLE\x10\x8b\x80\x80 \"\xa7\x01\n\x0fGoToPoseRequest\x12\x0c\n\x04x_mm\x18\x01 \x01(\x02\x12\x0c\n\x04y_mm\x18\x02 \x01(\x02\x12\x0b\n\x03rad\x18\x03 \x01(\x02\x12\x46\n\x0bmotion_prof\x18\x04 \x01(\x0b\x32\x31.Anki.Vector.external_interface.PathMotionProfile\x12\x0e\n\x06id_tag\x18\x05 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x06 \x01(\x05\"\x90\x01\n\x10GoToPoseResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\xd0\x02\n\x13\x44ockWithCubeRequest\x12\x11\n\tobject_id\x18\x01 \x01(\x05\x12\x1f\n\x17\x64istance_from_marker_mm\x18\x02 \x01(\x02\x12\x1a\n\x12\x61pproach_angle_rad\x18\x03 \x01(\x02\x12\x45\n\x0e\x61lignment_type\x18\x04 \x01(\x0e\x32-.Anki.Vector.external_interface.AlignmentType\x12\x1a\n\x12use_approach_angle\x18\x05 \x01(\x08\x12\x19\n\x11use_pre_dock_pose\x18\x06 \x01(\x08\x12\x46\n\x0bmotion_prof\x18\x07 \x01(\x0b\x32\x31.Anki.Vector.external_interface.PathMotionProfile\x12\x0e\n\x06id_tag\x18\x08 \x01(\x05\x12\x13\n\x0bnum_retries\x18\t \x01(\x05\"\x94\x01\n\x14\x44ockWithCubeResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\x7f\n\x14\x44riveStraightRequest\x12\x12\n\nspeed_mmps\x18\x01 \x01(\x02\x12\x0f\n\x07\x64ist_mm\x18\x02 \x01(\x02\x12\x1d\n\x15should_play_animation\x18\x03 \x01(\x08\x12\x0e\n\x06id_tag\x18\x04 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x05 \x01(\x05\"\x95\x01\n\x15\x44riveStraightResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\xa9\x01\n\x12TurnInPlaceRequest\x12\x11\n\tangle_rad\x18\x01 \x01(\x02\x12\x19\n\x11speed_rad_per_sec\x18\x02 \x01(\x02\x12\x1a\n\x12\x61\x63\x63\x65l_rad_per_sec2\x18\x03 \x01(\x02\x12\x0f\n\x07tol_rad\x18\x04 \x01(\x02\x12\x13\n\x0bis_absolute\x18\x05 \x01(\r\x12\x0e\n\x06id_tag\x18\x06 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x07 \x01(\x05\"\x93\x01\n\x13TurnInPlaceResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\x9e\x01\n\x13SetHeadAngleRequest\x12\x11\n\tangle_rad\x18\x01 \x01(\x02\x12\x1d\n\x15max_speed_rad_per_sec\x18\x02 \x01(\x02\x12\x1a\n\x12\x61\x63\x63\x65l_rad_per_sec2\x18\x03 \x01(\x02\x12\x14\n\x0c\x64uration_sec\x18\x04 \x01(\x02\x12\x0e\n\x06id_tag\x18\x05 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x06 \x01(\x05\"\x94\x01\n\x14SetHeadAngleResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\x9f\x01\n\x14SetLiftHeightRequest\x12\x11\n\theight_mm\x18\x01 \x01(\x02\x12\x1d\n\x15max_speed_rad_per_sec\x18\x02 \x01(\x02\x12\x1a\n\x12\x61\x63\x63\x65l_rad_per_sec2\x18\x03 \x01(\x02\x12\x14\n\x0c\x64uration_sec\x18\x04 \x01(\x02\x12\x0e\n\x06id_tag\x18\x05 \x01(\x05\x12\x13\n\x0bnum_retries\x18\x06 \x01(\x05\"\x95\x01\n\x15SetLiftHeightResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12<\n\x06result\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.ActionResult\"\xb9\x03\n\x0cNetworkStats\x12#\n\x1bg_net_stat1_num_connections\x18\x01 \x01(\x05\x12\x1f\n\x17g_net_stat2_latency_avg\x18\x02 \x01(\x02\x12\x1e\n\x16g_net_stat3_latency_sd\x18\x03 \x01(\x02\x12\x1f\n\x17g_net_stat4_latency_min\x18\x04 \x01(\x02\x12\x1f\n\x17g_net_stat5_latency_max\x18\x05 \x01(\x02\x12#\n\x1bg_net_stat6_ping_arrived_pc\x18\x06 \x01(\x02\x12%\n\x1dg_net_stat7_ext_queued_avg_ms\x18\x07 \x01(\x02\x12%\n\x1dg_net_stat8_ext_queued_min_ms\x18\x08 \x01(\x02\x12%\n\x1dg_net_stat9_ext_queued_max_ms\x18\t \x01(\x02\x12!\n\x19g_net_stata_queued_avg_ms\x18\n \x01(\x02\x12!\n\x19g_net_statb_queued_min_ms\x18\x0b \x01(\x02\x12!\n\x19g_net_statc_queued_max_ms\x18\x0c \x01(\x02\"\x15\n\x13\x42\x61tteryStateRequest\"\xc9\x02\n\x14\x42\x61tteryStateResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x43\n\rbattery_level\x18\x02 \x01(\x0e\x32,.Anki.Vector.external_interface.BatteryLevel\x12\x15\n\rbattery_volts\x18\x03 \x01(\x02\x12\x13\n\x0bis_charging\x18\x04 \x01(\x08\x12\x1e\n\x16is_on_charger_platform\x18\x05 \x01(\x08\x12\x1d\n\x15suggested_charger_sec\x18\x06 \x01(\x02\x12\x41\n\x0c\x63ube_battery\x18\x07 \x01(\x0b\x32+.Anki.Vector.external_interface.CubeBattery\"\xd3\x01\n\x0b\x43ubeBattery\x12K\n\x05level\x18\x01 \x01(\x0e\x32<.Anki.Vector.external_interface.CubeBattery.CubeBatteryLevel\x12\x12\n\nfactory_id\x18\x02 \x01(\t\x12\x15\n\rbattery_volts\x18\x03 \x01(\x02\x12#\n\x1btime_since_last_reading_sec\x18\x04 \x01(\x02\"\'\n\x10\x43ubeBatteryLevel\x12\x07\n\x03Low\x10\x00\x12\n\n\x06Normal\x10\x01\"\x15\n\x13VersionStateRequest\"\x83\x01\n\x14VersionStateResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x12\n\nos_version\x18\x02 \x01(\t\x12\x17\n\x0f\x65ngine_build_id\x18\x03 \x01(\t\"\x15\n\x13NetworkStateRequest\"\x9b\x01\n\x14NetworkStateResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x43\n\rnetwork_stats\x18\x02 \x01(\x0b\x32,.Anki.Vector.external_interface.NetworkStats\"Q\n\x0eSayTextRequest\x12\x0c\n\x04text\x18\x01 \x01(\t\x12\x18\n\x10use_vector_voice\x18\x02 \x01(\x08\x12\x17\n\x0f\x64uration_scalar\x18\x03 \x01(\x02\"\xf5\x01\n\x0fSayTextResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12M\n\x05state\x18\x02 \x01(\x0e\x32>.Anki.Vector.external_interface.SayTextResponse.UtteranceState\"S\n\x0eUtteranceState\x12\x0b\n\x07INVALID\x10\x00\x12\x0e\n\nGENERATING\x10\x01\x12\t\n\x05READY\x10\x02\x12\x0b\n\x07PLAYING\x10\x03\x12\x0c\n\x08\x46INISHED\x10\x04\"\x9b\x01\n\x0fStimulationInfo\x12\x16\n\x0e\x65motion_events\x18\x01 \x03(\t\x12\r\n\x05value\x18\x02 \x01(\x02\x12\x10\n\x08velocity\x18\x03 \x01(\x02\x12\r\n\x05\x61\x63\x63\x65l\x18\x04 \x01(\x02\x12\x1a\n\x12value_before_event\x18\x05 \x01(\x02\x12\x11\n\tmin_value\x18\x06 \x01(\x02\x12\x11\n\tmax_value\x18\x07 \x01(\x02\"Y\n\x14\x41udioSendModeRequest\x12\x41\n\x04mode\x18\x01 \x01(\x0e\x32\x33.Anki.Vector.external_interface.AudioProcessingMode\"Y\n\x14\x41udioSendModeChanged\x12\x41\n\x04mode\x18\x01 \x01(\x0e\x32\x33.Anki.Vector.external_interface.AudioProcessingMode\"\xe8\x01\n\nAudioChunk\x12\x18\n\x10robot_time_stamp\x18\x01 \x01(\r\x12\x10\n\x08group_id\x18\x02 \x01(\r\x12\x10\n\x08\x63hunk_id\x18\x03 \x01(\r\x12\x19\n\x11\x61udio_chunk_count\x18\x04 \x01(\r\x12\x14\n\x0csignal_power\x18\x05 \x01(\x0c\x12\x1b\n\x13\x64irection_strengths\x18\x06 \x01(\x0c\x12\x18\n\x10source_direction\x18\x07 \x01(\r\x12\x19\n\x11source_confidence\x18\x08 \x01(\r\x12\x19\n\x11noise_floor_power\x18\t \x01(\r\"\x12\n\x10\x41udioFeedRequest\"\xc8\x01\n\x11\x41udioFeedResponse\x12\x18\n\x10robot_time_stamp\x18\x01 \x01(\r\x12\x10\n\x08group_id\x18\x02 \x01(\r\x12\x14\n\x0csignal_power\x18\x03 \x01(\x0c\x12\x1b\n\x13\x64irection_strengths\x18\x04 \x01(\x0c\x12\x18\n\x10source_direction\x18\x05 \x01(\r\x12\x19\n\x11source_confidence\x18\x06 \x01(\r\x12\x19\n\x11noise_floor_power\x18\x07 \x01(\r:\x04\x80\xa6\x1d\x01\".\n\x1c\x45nableMarkerDetectionRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"_\n\x1d\x45nableMarkerDetectionResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\xb1\x01\n\x1a\x45nableFaceDetectionRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\x12\x1e\n\x16\x65nable_smile_detection\x18\x02 \x01(\x08\x12$\n\x1c\x65nable_expression_estimation\x18\x03 \x01(\x08\x12\x1e\n\x16\x65nable_blink_detection\x18\x04 \x01(\x08\x12\x1d\n\x15\x65nable_gaze_detection\x18\x05 \x01(\x08\"]\n\x1b\x45nableFaceDetectionResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\".\n\x1c\x45nableMotionDetectionRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"_\n\x1d\x45nableMotionDetectionResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\")\n\x17\x45nableMirrorModeRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"Z\n\x18\x45nableMirrorModeResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x14\n\x12MirrorModeDisabled\"-\n\x1b\x45nableImageStreamingRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"^\n\x1c\x45nableImageStreamingResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x19\n\x17VisionModesAutoDisabled\"\xd1\x03\n\nImageChunk\x12\x18\n\x10\x66rame_time_stamp\x18\x01 \x01(\r\x12\x10\n\x08image_id\x18\x02 \x01(\r\x12\r\n\x05width\x18\x03 \x01(\r\x12\x0e\n\x06height\x18\x04 \x01(\r\x12P\n\x0eimage_encoding\x18\x05 \x01(\x0e\x32\x38.Anki.Vector.external_interface.ImageChunk.ImageEncoding\x12\x15\n\rdisplay_index\x18\x06 \x01(\r\x12\x19\n\x11image_chunk_count\x18\x07 \x01(\r\x12\x10\n\x08\x63hunk_id\x18\x08 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\t \x01(\x0c\"\xd3\x01\n\rImageEncoding\x12\x17\n\x13NONE_IMAGE_ENCODING\x10\x00\x12\x0c\n\x08RAW_GRAY\x10\x01\x12\x0b\n\x07RAW_RGB\x10\x02\x12\x08\n\x04YUYV\x10\x03\x12\x0c\n\x08YUV420SP\x10\x04\x12\t\n\x05\x42\x41YER\x10\x05\x12\r\n\tJPEG_GRAY\x10\x06\x12\x0e\n\nJPEG_COLOR\x10\x07\x12\x19\n\x15JPEG_COLOR_HALF_WIDTH\x10\x08\x12\x17\n\x13JPEG_MINIMIZED_GRAY\x10\t\x12\x18\n\x14JPEG_MINIMIZED_COLOR\x10\n\"\x13\n\x11\x43\x61meraFeedRequest\"\xa6\x01\n\x12\x43\x61meraFeedResponse\x12\x18\n\x10\x66rame_time_stamp\x18\x01 \x01(\r\x12\x10\n\x08image_id\x18\x02 \x01(\r\x12P\n\x0eimage_encoding\x18\x03 \x01(\x0e\x32\x38.Anki.Vector.external_interface.ImageChunk.ImageEncoding\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c:\x04\x80\xa6\x1d\x01\"5\n\x12SetEyeColorRequest\x12\x0b\n\x03hue\x18\x01 \x01(\x02\x12\x12\n\nsaturation\x18\x02 \x01(\x02\"U\n\x13SetEyeColorResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x96\x01\n\x18SDKInitializationRequest\x12\x1a\n\x12sdk_module_version\x18\x01 \x01(\t\x12\x16\n\x0epython_version\x18\x02 \x01(\t\x12\x1d\n\x15python_implementation\x18\x03 \x01(\t\x12\x12\n\nos_version\x18\x04 \x01(\t\x12\x13\n\x0b\x63pu_version\x18\x05 \x01(\t\"[\n\x19SDKInitializationResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus*\xdc\x04\n\x0bRobotStatus\x12\x15\n\x11ROBOT_STATUS_NONE\x10\x00\x12\x1a\n\x16ROBOT_STATUS_IS_MOVING\x10\x01\x12\"\n\x1eROBOT_STATUS_IS_CARRYING_BLOCK\x10\x02\x12&\n\"ROBOT_STATUS_IS_PICKING_OR_PLACING\x10\x04\x12\x1d\n\x19ROBOT_STATUS_IS_PICKED_UP\x10\x08\x12\"\n\x1eROBOT_STATUS_IS_BUTTON_PRESSED\x10\x10\x12\x1b\n\x17ROBOT_STATUS_IS_FALLING\x10 \x12\x1d\n\x19ROBOT_STATUS_IS_ANIMATING\x10@\x12\x1c\n\x17ROBOT_STATUS_IS_PATHING\x10\x80\x01\x12\x1d\n\x18ROBOT_STATUS_LIFT_IN_POS\x10\x80\x02\x12\x1d\n\x18ROBOT_STATUS_HEAD_IN_POS\x10\x80\x04\x12!\n\x1cROBOT_STATUS_CALM_POWER_MODE\x10\x80\x08\x12\x1f\n\x1aROBOT_STATUS_IS_ON_CHARGER\x10\x80 \x12\x1d\n\x18ROBOT_STATUS_IS_CHARGING\x10\x80@\x12!\n\x1bROBOT_STATUS_CLIFF_DETECTED\x10\x80\x80\x01\x12$\n\x1eROBOT_STATUS_ARE_WHEELS_MOVING\x10\x80\x80\x02\x12 \n\x1aROBOT_STATUS_IS_BEING_HELD\x10\x80\x80\x04\x12%\n\x1fROBOT_STATUS_IS_MOTION_DETECTED\x10\x80\x80\x08*\xbd\x01\n\x10\x46\x61\x63ialExpression\x12\x16\n\x12\x45XPRESSION_UNKNOWN\x10\x00\x12\x16\n\x12\x45XPRESSION_NEUTRAL\x10\x01\x12\x18\n\x14\x45XPRESSION_HAPPINESS\x10\x02\x12\x17\n\x13\x45XPRESSION_SURPRISE\x10\x03\x12\x14\n\x10\x45XPRESSION_ANGER\x10\x04\x12\x16\n\x12\x45XPRESSION_SADNESS\x10\x05\x12\x14\n\x10\x45XPRESSION_COUNT\x10\x05\x1a\x02\x10\x01*\xcc\x01\n\x14\x46\x61\x63\x65\x45nrollmentResult\x12\x0b\n\x07SUCCESS\x10\x00\x12\x12\n\x0eSAW_WRONG_FACE\x10\x01\x12\x16\n\x12SAW_MULTIPLE_FACES\x10\x02\x12\r\n\tTIMED_OUT\x10\x03\x12\x0f\n\x0bSAVE_FAILED\x10\x04\x12\x0e\n\nINCOMPLETE\x10\x05\x12\r\n\tCANCELLED\x10\x06\x12\x0f\n\x0bNAME_IN_USE\x10\x07\x12\x16\n\x12NAMED_STORAGE_FULL\x10\x08\x12\x13\n\x0fUNKNOWN_FAILURE\x10\t*l\n\x0f\x42\x65haviorResults\x12\x1a\n\x16\x42\x45HAVIOR_INVALID_STATE\x10\x00\x12\x1b\n\x17\x42\x45HAVIOR_COMPLETE_STATE\x10\x01\x12 \n\x1c\x42\x45HAVIOR_WONT_ACTIVATE_STATE\x10\x02*S\n\x12\x41\x63tionTagConstants\x12\x13\n\x0fINVALID_SDK_TAG\x10\x00\x12\x13\n\rFIRST_SDK_TAG\x10\x81\x89z\x12\x13\n\x0cLAST_SDK_TAG\x10\xc0\x8d\xb7\x01*\x9e\x01\n\rAlignmentType\x12\x1a\n\x16\x41LIGNMENT_TYPE_UNKNOWN\x10\x00\x12\x1e\n\x1a\x41LIGNMENT_TYPE_LIFT_FINGER\x10\x01\x12\x1d\n\x19\x41LIGNMENT_TYPE_LIFT_PLATE\x10\x02\x12\x17\n\x13\x41LIGNMENT_TYPE_BODY\x10\x03\x12\x19\n\x15\x41LIGNMENT_TYPE_CUSTOM\x10\x04*s\n\x0c\x42\x61tteryLevel\x12\x19\n\x15\x42\x41TTERY_LEVEL_UNKNOWN\x10\x00\x12\x15\n\x11\x42\x41TTERY_LEVEL_LOW\x10\x01\x12\x19\n\x15\x42\x41TTERY_LEVEL_NOMINAL\x10\x02\x12\x16\n\x12\x42\x41TTERY_LEVEL_FULL\x10\x03*\xcc\x01\n\x0e\x41udioConstants\x12\x18\n\x14\x41UDIO_CONSTANTS_NULL\x10\x00\x12\x1c\n\x18MIC_DETECTION_DIRECTIONS\x10\x0c\x12%\n SAMPLE_COUNTS_PER_ENGINE_MESSAGE\x10\xa0\x01\x12\"\n\x1dSAMPLE_COUNTS_PER_SDK_MESSAGE\x10\xc0\x0c\x12\x1b\n\x16MICROPHONE_SAMPLE_RATE\x10\x89z\x12\x1a\n\x15PROCESSED_SAMPLE_RATE\x10\x80}*\x85\x01\n\x13\x41udioProcessingMode\x12\x11\n\rAUDIO_UNKNOWN\x10\x00\x12\r\n\tAUDIO_OFF\x10\x01\x12\x13\n\x0f\x41UDIO_FAST_MODE\x10\x02\x12\x1a\n\x16\x41UDIO_DIRECTIONAL_MODE\x10\x03\x12\x1b\n\x17\x41UDIO_VOICE_DETECT_MODE\x10\x04\x62\x06proto3') , - dependencies=[anki__vector_dot_messaging_dot_response__status__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_extensions__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_onboarding__steps__pb2.DESCRIPTOR,]) + dependencies=[anki__vector_dot_messaging_dot_response__status__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_extensions__pb2.DESCRIPTOR,]) +_ROBOTSTATUS = _descriptor.EnumDescriptor( + name='RobotStatus', + full_name='Anki.Vector.external_interface.RobotStatus', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_NONE', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_MOVING', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_CARRYING_BLOCK', index=2, number=2, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_PICKING_OR_PLACING', index=3, number=4, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_PICKED_UP', index=4, number=8, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_BUTTON_PRESSED', index=5, number=16, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_FALLING', index=6, number=32, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_ANIMATING', index=7, number=64, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_PATHING', index=8, number=128, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_LIFT_IN_POS', index=9, number=256, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_HEAD_IN_POS', index=10, number=512, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_CALM_POWER_MODE', index=11, number=1024, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_ON_CHARGER', index=12, number=4096, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_CHARGING', index=13, number=8192, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_CLIFF_DETECTED', index=14, number=16384, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_ARE_WHEELS_MOVING', index=15, number=32768, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_BEING_HELD', index=16, number=65536, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ROBOT_STATUS_IS_MOTION_DETECTED', index=17, number=131072, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=15131, + serialized_end=15735, +) +_sym_db.RegisterEnumDescriptor(_ROBOTSTATUS) + +RobotStatus = enum_type_wrapper.EnumTypeWrapper(_ROBOTSTATUS) _FACIALEXPRESSION = _descriptor.EnumDescriptor( name='FacialExpression', full_name='Anki.Vector.external_interface.FacialExpression', @@ -64,8 +150,8 @@ ], containing_type=None, options=_descriptor._ParseOptions(descriptor_pb2.EnumOptions(), _b('\020\001')), - serialized_start=15177, - serialized_end=15366, + serialized_start=15738, + serialized_end=15927, ) _sym_db.RegisterEnumDescriptor(_FACIALEXPRESSION) @@ -119,8 +205,8 @@ ], containing_type=None, options=None, - serialized_start=15369, - serialized_end=15573, + serialized_start=15930, + serialized_end=16134, ) _sym_db.RegisterEnumDescriptor(_FACEENROLLMENTRESULT) @@ -146,8 +232,8 @@ ], containing_type=None, options=None, - serialized_start=15575, - serialized_end=15683, + serialized_start=16136, + serialized_end=16244, ) _sym_db.RegisterEnumDescriptor(_BEHAVIORRESULTS) @@ -173,8 +259,8 @@ ], containing_type=None, options=None, - serialized_start=15685, - serialized_end=15768, + serialized_start=16246, + serialized_end=16329, ) _sym_db.RegisterEnumDescriptor(_ACTIONTAGCONSTANTS) @@ -208,8 +294,8 @@ ], containing_type=None, options=None, - serialized_start=15771, - serialized_end=15929, + serialized_start=16332, + serialized_end=16490, ) _sym_db.RegisterEnumDescriptor(_ALIGNMENTTYPE) @@ -239,8 +325,8 @@ ], containing_type=None, options=None, - serialized_start=15931, - serialized_end=16046, + serialized_start=16492, + serialized_end=16607, ) _sym_db.RegisterEnumDescriptor(_BATTERYLEVEL) @@ -278,8 +364,8 @@ ], containing_type=None, options=None, - serialized_start=16049, - serialized_end=16253, + serialized_start=16610, + serialized_end=16814, ) _sym_db.RegisterEnumDescriptor(_AUDIOCONSTANTS) @@ -313,12 +399,30 @@ ], containing_type=None, options=None, - serialized_start=16256, - serialized_end=16389, + serialized_start=16817, + serialized_end=16950, ) _sym_db.RegisterEnumDescriptor(_AUDIOPROCESSINGMODE) AudioProcessingMode = enum_type_wrapper.EnumTypeWrapper(_AUDIOPROCESSINGMODE) +ROBOT_STATUS_NONE = 0 +ROBOT_STATUS_IS_MOVING = 1 +ROBOT_STATUS_IS_CARRYING_BLOCK = 2 +ROBOT_STATUS_IS_PICKING_OR_PLACING = 4 +ROBOT_STATUS_IS_PICKED_UP = 8 +ROBOT_STATUS_IS_BUTTON_PRESSED = 16 +ROBOT_STATUS_IS_FALLING = 32 +ROBOT_STATUS_IS_ANIMATING = 64 +ROBOT_STATUS_IS_PATHING = 128 +ROBOT_STATUS_LIFT_IN_POS = 256 +ROBOT_STATUS_HEAD_IN_POS = 512 +ROBOT_STATUS_CALM_POWER_MODE = 1024 +ROBOT_STATUS_IS_ON_CHARGER = 4096 +ROBOT_STATUS_IS_CHARGING = 8192 +ROBOT_STATUS_CLIFF_DETECTED = 16384 +ROBOT_STATUS_ARE_WHEELS_MOVING = 32768 +ROBOT_STATUS_IS_BEING_HELD = 65536 +ROBOT_STATUS_IS_MOTION_DETECTED = 131072 EXPRESSION_UNKNOWN = 0 EXPRESSION_NEUTRAL = 1 EXPRESSION_HAPPINESS = 2 @@ -585,8 +689,8 @@ ], containing_type=None, options=None, - serialized_start=7158, - serialized_end=8720, + serialized_start=7112, + serialized_end=8674, ) _sym_db.RegisterEnumDescriptor(_ACTIONRESULT_ACTIONRESULTCODE) @@ -607,8 +711,8 @@ ], containing_type=None, options=None, - serialized_start=11730, - serialized_end=11769, + serialized_start=11684, + serialized_end=11723, ) _sym_db.RegisterEnumDescriptor(_CUBEBATTERY_CUBEBATTERYLEVEL) @@ -641,8 +745,8 @@ ], containing_type=None, options=None, - serialized_start=12355, - serialized_end=12438, + serialized_start=12309, + serialized_end=12392, ) _sym_db.RegisterEnumDescriptor(_SAYTEXTRESPONSE_UTTERANCESTATE) @@ -699,8 +803,8 @@ ], containing_type=None, options=None, - serialized_start=14385, - serialized_end=14596, + serialized_start=14339, + serialized_end=14550, ) _sym_db.RegisterEnumDescriptor(_IMAGECHUNK_IMAGEENCODING) @@ -724,8 +828,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=203, - serialized_end=218, + serialized_start=157, + serialized_end=172, ) @@ -755,8 +859,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=220, - serialized_end=245, + serialized_start=174, + serialized_end=199, ) @@ -807,8 +911,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=247, - serialized_end=371, + serialized_start=201, + serialized_end=325, ) @@ -838,8 +942,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=373, - serialized_end=458, + serialized_start=327, + serialized_end=412, ) @@ -869,8 +973,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=460, - serialized_end=504, + serialized_start=414, + serialized_end=458, ) @@ -900,8 +1004,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=506, - serialized_end=588, + serialized_start=460, + serialized_end=542, ) @@ -931,8 +1035,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=590, - serialized_end=634, + serialized_start=544, + serialized_end=588, ) @@ -962,8 +1066,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=636, - serialized_end=718, + serialized_start=590, + serialized_end=672, ) @@ -1021,8 +1125,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=721, - serialized_end=901, + serialized_start=675, + serialized_end=855, ) @@ -1059,8 +1163,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=904, - serialized_end=1056, + serialized_start=858, + serialized_end=1010, ) @@ -1083,8 +1187,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1058, - serialized_end=1081, + serialized_start=1012, + serialized_end=1035, ) @@ -1121,8 +1225,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1084, - serialized_end=1240, + serialized_start=1038, + serialized_end=1194, ) @@ -1166,8 +1270,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1242, - serialized_end=1337, + serialized_start=1196, + serialized_end=1291, ) @@ -1197,8 +1301,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1339, - serialized_end=1432, + serialized_start=1293, + serialized_end=1386, ) @@ -1221,8 +1325,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1434, - serialized_end=1461, + serialized_start=1388, + serialized_end=1415, ) @@ -1245,8 +1349,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1463, - serialized_end=1491, + serialized_start=1417, + serialized_end=1445, ) @@ -1293,8 +1397,8 @@ name='status_type', full_name='Anki.Vector.external_interface.Status.status_type', index=0, containing_type=None, fields=[]), ], - serialized_start=1494, - serialized_end=1813, + serialized_start=1448, + serialized_end=1767, ) @@ -1334,8 +1438,8 @@ name='wake_word_type', full_name='Anki.Vector.external_interface.WakeWord.wake_word_type', index=0, containing_type=None, fields=[]), ], - serialized_start=1816, - serialized_end=1988, + serialized_start=1770, + serialized_end=1942, ) @@ -1372,8 +1476,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1990, - serialized_end=2088, + serialized_start=1944, + serialized_end=2042, ) @@ -1452,8 +1556,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2090, - serialized_end=2202, + serialized_start=2044, + serialized_end=2156, ) @@ -1497,8 +1601,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2204, - serialized_end=2248, + serialized_start=2158, + serialized_end=2202, ) @@ -1542,8 +1646,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2250, - serialized_end=2293, + serialized_start=2204, + serialized_end=2247, ) @@ -1608,8 +1712,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2296, - serialized_end=2459, + serialized_start=2250, + serialized_end=2413, ) @@ -1646,8 +1750,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2461, - serialized_end=2523, + serialized_start=2415, + serialized_end=2477, ) @@ -1789,8 +1893,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2526, - serialized_end=3168, + serialized_start=2480, + serialized_end=3122, ) @@ -1827,8 +1931,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3170, - serialized_end=3203, + serialized_start=3124, + serialized_end=3157, ) @@ -1879,8 +1983,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3205, - serialized_end=3286, + serialized_start=3159, + serialized_end=3240, ) @@ -1980,8 +2084,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3289, - serialized_end=3811, + serialized_start=3243, + serialized_end=3765, ) @@ -2018,8 +2122,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3813, - serialized_end=3873, + serialized_start=3767, + serialized_end=3827, ) @@ -2056,8 +2160,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3875, - serialized_end=3924, + serialized_start=3829, + serialized_end=3878, ) @@ -2080,8 +2184,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3926, - serialized_end=3943, + serialized_start=3880, + serialized_end=3897, ) @@ -2111,8 +2215,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3945, - serialized_end=4028, + serialized_start=3899, + serialized_end=3982, ) @@ -2156,8 +2260,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4030, - serialized_end=4156, + serialized_start=3984, + serialized_end=4110, ) @@ -2180,8 +2284,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4158, - serialized_end=4187, + serialized_start=4112, + serialized_end=4141, ) @@ -2211,8 +2315,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4189, - serialized_end=4283, + serialized_start=4143, + serialized_end=4237, ) @@ -2235,8 +2339,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4285, - serialized_end=4314, + serialized_start=4239, + serialized_end=4268, ) @@ -2301,8 +2405,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4317, - serialized_end=4511, + serialized_start=4271, + serialized_end=4465, ) @@ -2339,8 +2443,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4513, - serialized_end=4570, + serialized_start=4467, + serialized_end=4524, ) @@ -2377,8 +2481,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4573, - serialized_end=4731, + serialized_start=4527, + serialized_end=4685, ) @@ -2422,8 +2526,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4733, - serialized_end=4817, + serialized_start=4687, + serialized_end=4771, ) @@ -2453,8 +2557,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4819, - serialized_end=4915, + serialized_start=4773, + serialized_end=4869, ) @@ -2484,8 +2588,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4917, - serialized_end=4964, + serialized_start=4871, + serialized_end=4918, ) @@ -2515,8 +2619,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4966, - serialized_end=5061, + serialized_start=4920, + serialized_end=5015, ) @@ -2539,8 +2643,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5063, - serialized_end=5093, + serialized_start=5017, + serialized_end=5047, ) @@ -2570,8 +2674,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5095, - serialized_end=5190, + serialized_start=5049, + serialized_end=5144, ) @@ -2636,8 +2740,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5193, - serialized_end=5329, + serialized_start=5147, + serialized_end=5283, ) @@ -2667,8 +2771,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5331, - serialized_end=5420, + serialized_start=5285, + serialized_end=5374, ) @@ -2691,8 +2795,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5422, - serialized_end=5446, + serialized_start=5376, + serialized_end=5400, ) @@ -2729,8 +2833,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5449, - serialized_end=5603, + serialized_start=5403, + serialized_end=5557, ) @@ -2753,8 +2857,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5605, - serialized_end=5628, + serialized_start=5559, + serialized_end=5582, ) @@ -2791,8 +2895,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5631, - serialized_end=5784, + serialized_start=5585, + serialized_end=5738, ) @@ -2815,8 +2919,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5786, - serialized_end=5801, + serialized_start=5740, + serialized_end=5755, ) @@ -2853,8 +2957,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5803, - serialized_end=5859, + serialized_start=5757, + serialized_end=5813, ) @@ -2905,8 +3009,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5861, - serialized_end=5971, + serialized_start=5815, + serialized_end=5925, ) @@ -2929,8 +3033,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5973, - serialized_end=5992, + serialized_start=5927, + serialized_end=5946, ) @@ -2967,8 +3071,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5995, - serialized_end=6143, + serialized_start=5949, + serialized_end=6097, ) @@ -2998,8 +3102,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6145, - serialized_end=6177, + serialized_start=6099, + serialized_end=6131, ) @@ -3036,8 +3140,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6179, - serialized_end=6233, + serialized_start=6133, + serialized_end=6187, ) @@ -3081,8 +3185,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6235, - serialized_end=6346, + serialized_start=6189, + serialized_end=6300, ) @@ -3112,8 +3216,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6348, - serialized_end=6384, + serialized_start=6302, + serialized_end=6338, ) @@ -3150,8 +3254,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6386, - serialized_end=6444, + serialized_start=6340, + serialized_end=6398, ) @@ -3195,8 +3299,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6446, - serialized_end=6561, + serialized_start=6400, + serialized_end=6515, ) @@ -3226,8 +3330,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6563, - serialized_end=6601, + serialized_start=6517, + serialized_end=6555, ) @@ -3264,8 +3368,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6603, - serialized_end=6705, + serialized_start=6557, + serialized_end=6659, ) @@ -3295,8 +3399,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6707, - serialized_end=6737, + serialized_start=6661, + serialized_end=6691, ) @@ -3396,8 +3500,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6740, - serialized_end=7061, + serialized_start=6694, + serialized_end=7015, ) @@ -3428,8 +3532,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=7064, - serialized_end=8720, + serialized_start=7018, + serialized_end=8674, ) @@ -3494,8 +3598,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=8723, - serialized_end=8890, + serialized_start=8677, + serialized_end=8844, ) @@ -3532,8 +3636,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=8893, - serialized_end=9037, + serialized_start=8847, + serialized_end=8991, ) @@ -3619,8 +3723,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=9040, - serialized_end=9376, + serialized_start=8994, + serialized_end=9330, ) @@ -3657,8 +3761,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=9379, - serialized_end=9527, + serialized_start=9333, + serialized_end=9481, ) @@ -3716,8 +3820,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=9529, - serialized_end=9656, + serialized_start=9483, + serialized_end=9610, ) @@ -3754,8 +3858,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=9659, - serialized_end=9808, + serialized_start=9613, + serialized_end=9762, ) @@ -3827,8 +3931,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=9811, - serialized_end=9980, + serialized_start=9765, + serialized_end=9934, ) @@ -3865,8 +3969,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=9983, - serialized_end=10130, + serialized_start=9937, + serialized_end=10084, ) @@ -3931,8 +4035,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=10133, - serialized_end=10291, + serialized_start=10087, + serialized_end=10245, ) @@ -3969,8 +4073,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=10294, - serialized_end=10442, + serialized_start=10248, + serialized_end=10396, ) @@ -4035,8 +4139,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=10445, - serialized_end=10604, + serialized_start=10399, + serialized_end=10558, ) @@ -4073,8 +4177,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=10607, - serialized_end=10756, + serialized_start=10561, + serialized_end=10710, ) @@ -4181,8 +4285,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=10759, - serialized_end=11200, + serialized_start=10713, + serialized_end=11154, ) @@ -4205,8 +4309,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=11202, - serialized_end=11223, + serialized_start=11156, + serialized_end=11177, ) @@ -4278,8 +4382,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=11226, - serialized_end=11555, + serialized_start=11180, + serialized_end=11509, ) @@ -4331,8 +4435,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=11558, - serialized_end=11769, + serialized_start=11512, + serialized_end=11723, ) @@ -4355,8 +4459,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=11771, - serialized_end=11792, + serialized_start=11725, + serialized_end=11746, ) @@ -4400,8 +4504,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=11795, - serialized_end=11926, + serialized_start=11749, + serialized_end=11880, ) @@ -4424,8 +4528,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=11928, - serialized_end=11949, + serialized_start=11882, + serialized_end=11903, ) @@ -4462,8 +4566,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=11952, - serialized_end=12107, + serialized_start=11906, + serialized_end=12061, ) @@ -4507,8 +4611,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=12109, - serialized_end=12190, + serialized_start=12063, + serialized_end=12144, ) @@ -4546,8 +4650,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=12193, - serialized_end=12438, + serialized_start=12147, + serialized_end=12392, ) @@ -4619,8 +4723,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=12441, - serialized_end=12596, + serialized_start=12395, + serialized_end=12550, ) @@ -4650,8 +4754,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=12598, - serialized_end=12687, + serialized_start=12552, + serialized_end=12641, ) @@ -4681,8 +4785,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=12689, - serialized_end=12778, + serialized_start=12643, + serialized_end=12732, ) @@ -4768,8 +4872,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=12781, - serialized_end=13013, + serialized_start=12735, + serialized_end=12967, ) @@ -4792,8 +4896,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13015, - serialized_end=13033, + serialized_start=12969, + serialized_end=12987, ) @@ -4865,8 +4969,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13036, - serialized_end=13236, + serialized_start=12990, + serialized_end=13190, ) @@ -4896,8 +5000,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13238, - serialized_end=13284, + serialized_start=13192, + serialized_end=13238, ) @@ -4927,8 +5031,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13286, - serialized_end=13381, + serialized_start=13240, + serialized_end=13335, ) @@ -4986,8 +5090,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13384, - serialized_end=13561, + serialized_start=13338, + serialized_end=13515, ) @@ -5017,8 +5121,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13563, - serialized_end=13656, + serialized_start=13517, + serialized_end=13610, ) @@ -5048,8 +5152,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13658, - serialized_end=13704, + serialized_start=13612, + serialized_end=13658, ) @@ -5079,8 +5183,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13706, - serialized_end=13801, + serialized_start=13660, + serialized_end=13755, ) @@ -5110,8 +5214,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13803, - serialized_end=13844, + serialized_start=13757, + serialized_end=13798, ) @@ -5141,8 +5245,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13846, - serialized_end=13936, + serialized_start=13800, + serialized_end=13890, ) @@ -5165,8 +5269,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13938, - serialized_end=13958, + serialized_start=13892, + serialized_end=13912, ) @@ -5196,8 +5300,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=13960, - serialized_end=14005, + serialized_start=13914, + serialized_end=13959, ) @@ -5227,8 +5331,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=14007, - serialized_end=14101, + serialized_start=13961, + serialized_end=14055, ) @@ -5251,8 +5355,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=14103, - serialized_end=14128, + serialized_start=14057, + serialized_end=14082, ) @@ -5339,8 +5443,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=14131, - serialized_end=14596, + serialized_start=14085, + serialized_end=14550, ) @@ -5363,8 +5467,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=14598, - serialized_end=14617, + serialized_start=14552, + serialized_end=14571, ) @@ -5415,8 +5519,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=14620, - serialized_end=14786, + serialized_start=14574, + serialized_end=14740, ) @@ -5453,8 +5557,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=14788, - serialized_end=14841, + serialized_start=14742, + serialized_end=14795, ) @@ -5484,8 +5588,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=14843, - serialized_end=14928, + serialized_start=14797, + serialized_end=14882, ) @@ -5543,8 +5647,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=14931, - serialized_end=15081, + serialized_start=14885, + serialized_end=15035, ) @@ -5574,8 +5678,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=15083, - serialized_end=15174, + serialized_start=15037, + serialized_end=15128, ) _DRIVEWHEELSRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS @@ -5792,6 +5896,7 @@ DESCRIPTOR.message_types_by_name['SetEyeColorResponse'] = _SETEYECOLORRESPONSE DESCRIPTOR.message_types_by_name['SDKInitializationRequest'] = _SDKINITIALIZATIONREQUEST DESCRIPTOR.message_types_by_name['SDKInitializationResponse'] = _SDKINITIALIZATIONRESPONSE +DESCRIPTOR.enum_types_by_name['RobotStatus'] = _ROBOTSTATUS DESCRIPTOR.enum_types_by_name['FacialExpression'] = _FACIALEXPRESSION DESCRIPTOR.enum_types_by_name['FaceEnrollmentResult'] = _FACEENROLLMENTRESULT DESCRIPTOR.enum_types_by_name['BehaviorResults'] = _BEHAVIORRESULTS diff --git a/anki_vector/messaging/onboarding_steps.proto b/anki_vector/messaging/onboarding_steps.proto deleted file mode 100644 index 37c40bb..0000000 --- a/anki_vector/messaging/onboarding_steps.proto +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2018 Anki, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License in the file LICENSE.txt or at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Onboarding messages - -syntax = "proto3"; - -package Anki.Vector.external_interface; - diff --git a/anki_vector/messaging/onboarding_steps_pb2.py b/anki_vector/messaging/onboarding_steps_pb2.py deleted file mode 100644 index 014a556..0000000 --- a/anki_vector/messaging/onboarding_steps_pb2.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/onboarding_steps.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -from google.protobuf import descriptor_pb2 -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='anki_vector/messaging/onboarding_steps.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - serialized_pb=_b('\n,anki_vector/messaging/onboarding_steps.proto\x12\x1e\x41nki.Vector.external_interfaceb\x06proto3') -) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/onboarding_steps_pb2_grpc.py b/anki_vector/messaging/onboarding_steps_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/onboarding_steps_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/anki_vector/messaging/settings.proto b/anki_vector/messaging/settings.proto index 0e6f8f4..d3d35cf 100644 --- a/anki_vector/messaging/settings.proto +++ b/anki_vector/messaging/settings.proto @@ -18,7 +18,6 @@ syntax = "proto3"; package Anki.Vector.external_interface; -import "anki_vector/messaging/response_status.proto"; enum ApiVersion { INVALID = 0; diff --git a/anki_vector/messaging/settings_pb2.py b/anki_vector/messaging/settings_pb2.py index edccd54..f5dff10 100644 --- a/anki_vector/messaging/settings_pb2.py +++ b/anki_vector/messaging/settings_pb2.py @@ -14,16 +14,14 @@ _sym_db = _symbol_database.Default() -from anki_vector.messaging import response_status_pb2 as anki__vector_dot_messaging_dot_response__status__pb2 DESCRIPTOR = _descriptor.FileDescriptor( name='anki_vector/messaging/settings.proto', package='Anki.Vector.external_interface', syntax='proto3', - serialized_pb=_b('\n$anki_vector/messaging/settings.proto\x12\x1e\x41nki.Vector.external_interface\x1a+anki_vector/messaging/response_status.proto*%\n\nApiVersion\x12\x0b\n\x07INVALID\x10\x00\x12\n\n\x06LATEST\x10\x01*R\n\x06Volume\x12\x08\n\x04MUTE\x10\x00\x12\x07\n\x03LOW\x10\x01\x12\x0e\n\nMEDIUM_LOW\x10\x02\x12\n\n\x06MEDIUM\x10\x03\x12\x0f\n\x0bMEDIUM_HIGH\x10\x04\x12\x08\n\x04HIGH\x10\x05\x62\x06proto3') - , - dependencies=[anki__vector_dot_messaging_dot_response__status__pb2.DESCRIPTOR,]) + serialized_pb=_b('\n$anki_vector/messaging/settings.proto\x12\x1e\x41nki.Vector.external_interface*%\n\nApiVersion\x12\x0b\n\x07INVALID\x10\x00\x12\n\n\x06LATEST\x10\x01*R\n\x06Volume\x12\x08\n\x04MUTE\x10\x00\x12\x07\n\x03LOW\x10\x01\x12\x0e\n\nMEDIUM_LOW\x10\x02\x12\n\n\x06MEDIUM\x10\x03\x12\x0f\n\x0bMEDIUM_HIGH\x10\x04\x12\x08\n\x04HIGH\x10\x05\x62\x06proto3') +) _APIVERSION = _descriptor.EnumDescriptor( name='ApiVersion', @@ -42,8 +40,8 @@ ], containing_type=None, options=None, - serialized_start=117, - serialized_end=154, + serialized_start=72, + serialized_end=109, ) _sym_db.RegisterEnumDescriptor(_APIVERSION) @@ -81,8 +79,8 @@ ], containing_type=None, options=None, - serialized_start=156, - serialized_end=238, + serialized_start=111, + serialized_end=193, ) _sym_db.RegisterEnumDescriptor(_VOLUME) From 316fafe342bda41baa5deae64f4de8818736dc39 Mon Sep 17 00:00:00 2001 From: Michelle Sintov Date: Sat, 1 Dec 2018 17:40:57 -0800 Subject: [PATCH 04/13] VIC-12067 Drive off charger first to prevent proximity mapper failing #29 --- examples/apps/proximity_mapper/proximity_mapper.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/apps/proximity_mapper/proximity_mapper.py b/examples/apps/proximity_mapper/proximity_mapper.py index a472b27..c423750 100755 --- a/examples/apps/proximity_mapper/proximity_mapper.py +++ b/examples/apps/proximity_mapper/proximity_mapper.py @@ -317,6 +317,8 @@ def open_point_sort_func(position: Vector3): # Creates the 3D Viewer for the connected robot. viewerInstance = OpenGLViewer(robot=robotInstance, show_viewer_controls=False) + robotInstance.behavior.drive_off_charger() + # The OpenGLViewer has to run on the main thread, so control is given to # it via the blocking 'run' call. The core loop of our program is injected into # this call to run in parallel on a secondary thread. When the injected function From 9b69fd1f7eb860350703c8ccad1059722e6902ce Mon Sep 17 00:00:00 2001 From: Keith Prickett Date: Sun, 2 Dec 2018 20:49:55 -0800 Subject: [PATCH 05/13] VIC-7776 Python SDK: Add status class (#24) * VIC-7776 Python SDK: Add status class Adds properties for each status bit in the bitfield coming from the robot. --- anki_vector/robot.py | 43 ++---- anki_vector/status.py | 314 ++++++++++++++++++++++++++++++++++++++++++ anki_vector/util.py | 4 +- docs/source/api.rst | 1 + 4 files changed, 331 insertions(+), 31 deletions(-) create mode 100644 anki_vector/status.py diff --git a/anki_vector/robot.py b/anki_vector/robot.py index a7a3395..2095675 100755 --- a/anki_vector/robot.py +++ b/anki_vector/robot.py @@ -27,7 +27,7 @@ from . import (animation, audio, behavior, camera, connection, events, exceptions, faces, motors, nav_map, screen, photos, proximity, - touch, util, viewer, vision, world) + status, touch, util, viewer, vision, world) from .messaging import protocol @@ -161,7 +161,7 @@ def __init__(self, self._head_tracking_object_id: float = None self._localized_to_object_id: float = None self._last_image_time_stamp: float = None - self._status: float = None + self._status: status.RobotStatus = status.RobotStatus() self.pending = [] self._enable_camera_feed = enable_camera_feed @@ -527,37 +527,22 @@ def last_image_time_stamp(self) -> int: return self._last_image_time_stamp @property - @util.block_while_none() - def status(self) -> float: - """Describes Vector's status. - - Possible values include: - | NoneRobotStatusFlag = 0 - | IS_MOVING = 0x1 - | IS_CARRYING_BLOCK = 0x2 - | IS_PICKING_OR_PLACING = 0x4 - | IS_PICKED_UP = 0x8 - | IS_BUTTON_PRESSED = 0x10 - | IS_FALLING = 0x20 - | IS_ANIMATING = 0x40 - | IS_PATHING = 0x80 - | LIFT_IN_POS = 0x100 - | HEAD_IN_POS = 0x200 - | CALM_POWER_MODE = 0x400 - | IS_BATTERY_DISCONNECTED = 0x800 - | IS_ON_CHARGER = 0x1000 - | IS_CHARGING = 0x2000 - | CLIFF_DETECTED = 0x4000 - | ARE_WHEELS_MOVING = 0x8000 - | IS_BEING_HELD = 0x10000 - | IS_MOTION_DETECTED = 0x20000 - | IS_BATTERY_OVERHEATED = 0x40000 + def status(self) -> status.RobotStatus: + """A property that exposes various status properties of the robot. + + This status provides a simple mechanism to, for example, detect if any + of Vector's motors are moving, determine if Vector is being held, or if + he is on the charger. The full list is available in the + :class:`RobotStatus ` class documentation. .. testcode:: import anki_vector with anki_vector.Robot() as robot: - current_status = robot.status + if robot.status.is_being_held: + print("Vector is being held!") + else: + print("Vector is not being held.") """ return self._status @@ -636,7 +621,7 @@ def _unpack_robot_state(self, _, msg): self._head_tracking_object_id = msg.head_tracking_object_id self._localized_to_object_id = msg.localized_to_object_id self._last_image_time_stamp = msg.last_image_time_stamp - self._status = msg.status + self._status.set(msg.status) def connect(self, timeout: int = 10) -> None: """Start the connection to Vector. diff --git a/anki_vector/status.py b/anki_vector/status.py new file mode 100644 index 0000000..88e4354 --- /dev/null +++ b/anki_vector/status.py @@ -0,0 +1,314 @@ +# Copyright (c) 2018 Anki, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License in the file LICENSE.txt or at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +.. _status: + +Robot Status class and exposed properties for Vector's various states. + +The :class:`RobotStatus` class in this module exposes properties +about the robot status like :py:attr:`is_charging `, +:py:attr:`is_being_held `, etc. +""" + +# __all__ should order by constants, event classes, other classes, functions. +__all__ = ['ROBOT_STATUS_NONE', 'ROBOT_STATUS_ARE_MOTORS_MOVING', 'ROBOT_STATUS_IS_CARRYING_BLOCK', + 'ROBOT_STATUS_IS_DOCKING_TO_MARKER', 'ROBOT_STATUS_IS_PICKED_UP', 'ROBOT_STATUS_IS_BUTTON_PRESSED', + 'ROBOT_STATUS_IS_FALLING', 'ROBOT_STATUS_IS_ANIMATING', 'ROBOT_STATUS_IS_PATHING', + 'ROBOT_STATUS_LIFT_IN_POS', 'ROBOT_STATUS_HEAD_IN_POS', 'ROBOT_STATUS_CALM_POWER_MODE', + 'ROBOT_STATUS_IS_ON_CHARGER', 'ROBOT_STATUS_IS_CHARGING', 'ROBOT_STATUS_CLIFF_DETECTED', + 'ROBOT_STATUS_ARE_WHEELS_MOVING', 'ROBOT_STATUS_IS_BEING_HELD', 'ROBOT_STATUS_IS_ROBOT_MOVING', + 'RobotStatus'] + +from . import util +from .messaging import protocol + +ROBOT_STATUS_NONE = protocol.RobotStatus.Value("ROBOT_STATUS_NONE") +ROBOT_STATUS_ARE_MOTORS_MOVING = protocol.RobotStatus.Value("ROBOT_STATUS_IS_MOVING") +ROBOT_STATUS_IS_CARRYING_BLOCK = protocol.RobotStatus.Value("ROBOT_STATUS_IS_CARRYING_BLOCK") +ROBOT_STATUS_IS_DOCKING_TO_MARKER = protocol.RobotStatus.Value("ROBOT_STATUS_IS_PICKING_OR_PLACING") +ROBOT_STATUS_IS_PICKED_UP = protocol.RobotStatus.Value("ROBOT_STATUS_IS_PICKED_UP") +ROBOT_STATUS_IS_BUTTON_PRESSED = protocol.RobotStatus.Value("ROBOT_STATUS_IS_BUTTON_PRESSED") +ROBOT_STATUS_IS_FALLING = protocol.RobotStatus.Value("ROBOT_STATUS_IS_FALLING") +ROBOT_STATUS_IS_ANIMATING = protocol.RobotStatus.Value("ROBOT_STATUS_IS_ANIMATING") +ROBOT_STATUS_IS_PATHING = protocol.RobotStatus.Value("ROBOT_STATUS_IS_PATHING") +ROBOT_STATUS_LIFT_IN_POS = protocol.RobotStatus.Value("ROBOT_STATUS_LIFT_IN_POS") +ROBOT_STATUS_HEAD_IN_POS = protocol.RobotStatus.Value("ROBOT_STATUS_HEAD_IN_POS") +ROBOT_STATUS_CALM_POWER_MODE = protocol.RobotStatus.Value("ROBOT_STATUS_CALM_POWER_MODE") +ROBOT_STATUS_IS_ON_CHARGER = protocol.RobotStatus.Value("ROBOT_STATUS_IS_ON_CHARGER") +ROBOT_STATUS_IS_CHARGING = protocol.RobotStatus.Value("ROBOT_STATUS_IS_CHARGING") +ROBOT_STATUS_CLIFF_DETECTED = protocol.RobotStatus.Value("ROBOT_STATUS_CLIFF_DETECTED") +ROBOT_STATUS_ARE_WHEELS_MOVING = protocol.RobotStatus.Value("ROBOT_STATUS_ARE_WHEELS_MOVING") +ROBOT_STATUS_IS_BEING_HELD = protocol.RobotStatus.Value("ROBOT_STATUS_IS_BEING_HELD") +ROBOT_STATUS_IS_ROBOT_MOVING = protocol.RobotStatus.Value("ROBOT_STATUS_IS_MOTION_DETECTED") + + +class RobotStatus(): + """A class to expose various status properties of the robot.""" + + def __init__(self): + # Default robot status + self._status: int = None + + def set(self, status: int): + self._status = status + + @util.block_while_none() + def __get(self) -> int: + return self._status + + @property + def are_motors_moving(self) -> bool: + """True if Vector is currently moving any of his motors (head, arm or + wheels/treads). + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.are_motors_moving: + print("Vector is moving.") + """ + return (self.__get() & ROBOT_STATUS_ARE_MOTORS_MOVING) != 0 + + @property + def is_carrying_block(self) -> bool: + """True if Vector is currently carrying a block. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_carrying_block: + print("Vector is carrying his block.") + """ + return (self.__get() & ROBOT_STATUS_IS_CARRYING_BLOCK) != 0 + + @property + def is_docking_to_marker(self) -> bool: + """True if Vector has seen a marker and is actively heading toward it + (for example his charger or cube). + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_docking_to_marker: + print("Vector has found a marker and is docking to it.") + """ + return (self.__get() & ROBOT_STATUS_IS_DOCKING_TO_MARKER) != 0 + + @property + def is_picked_up(self) -> bool: + """True if Vector is currently picked up (in the air). + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_picked_up: + print("Vector is picked up.") + """ + return (self.__get() & ROBOT_STATUS_IS_PICKED_UP) != 0 + + @property + def is_button_pressed(self) -> bool: + """True if Vector's button is pressed. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_button_pressed: + print("Vector's button was button pressed.") + """ + return (self.__get() & ROBOT_STATUS_IS_BUTTON_PRESSED) != 0 + + @property + def is_falling(self) -> bool: + """True if Vector is currently falling. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_falling: + print("Vector is falling.") + """ + return (self.__get() & ROBOT_STATUS_IS_FALLING) != 0 + + @property + def is_animating(self) -> bool: + """True if Vector is currently playing an animation. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_animating: + print("Vector is animating.") + """ + return (self.__get() & ROBOT_STATUS_IS_ANIMATING) != 0 + + @property + def is_pathing(self) -> bool: + """True if Vector is currently traversing a path. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_pathing: + print("Vector is traversing a path.") + """ + return (self.__get() & ROBOT_STATUS_IS_PATHING) != 0 + + @property + def is_lift_in_pos(self) -> bool: + """True if Vector's arm is in the desired position (False if still + trying to move it there). + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_lift_in_pos: + print("Vector's arm is in position.") + """ + return (self.__get() & ROBOT_STATUS_LIFT_IN_POS) != 0 + + @property + def is_head_in_pos(self) -> bool: + """True if Vector's head is in the desired position (False if still + trying to move there). + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_head_in_pos: + print("Vector's head is in position.") + """ + return (self.__get() & ROBOT_STATUS_HEAD_IN_POS) != 0 + + @property + def is_in_calm_power_mode(self) -> bool: + """True if Vector is in calm power mode. Calm power mode is generally + when Vector is sleeping or charging. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_in_calm_power_mode: + print("Vector is in calm power mode.") + """ + return (self.__get() & ROBOT_STATUS_CALM_POWER_MODE) != 0 + + @property + def is_on_charger(self) -> bool: + """True if Vector is currently on the charger. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_on_charger: + print("Vector is on the charger.") + """ + return (self.__get() & ROBOT_STATUS_IS_ON_CHARGER) != 0 + + @property + def is_charging(self) -> bool: + """True if Vector is currently charging. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_charging: + print("Vector is currently charging.") + """ + return (self.__get() & ROBOT_STATUS_IS_CHARGING) != 0 + + @property + def is_cliff_detected(self) -> bool: + """True if Vector detected a cliff using any of his four cliff sensors. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_cliff_detected: + print("Vector has detected a cliff.") + """ + return (self.__get() & ROBOT_STATUS_CLIFF_DETECTED) != 0 + + @property + def are_wheels_moving(self) -> bool: + """True if Vector's wheels/treads are currently moving. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.are_wheels_moving: + print("Vector's wheels are moving.") + """ + return (self.__get() & ROBOT_STATUS_ARE_WHEELS_MOVING) != 0 + + @property + def is_being_held(self) -> bool: + """True if Vector is being held. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_being_held: + print("Vector is being held.") + """ + return (self.__get() & ROBOT_STATUS_IS_BEING_HELD) != 0 + + @property + def is_robot_moving(self) -> bool: + """True if Vector is in motion. This includes any of his motors + (head, arm, wheels/tracks) and if he is being lifted, carried, + or falling. + + .. testcode:: + + import anki_vector + + with anki_vector.Robot() as robot: + if robot.status.is_robot_moving: + print("Vector has is in motion.") + """ + return (self.__get() & ROBOT_STATUS_IS_ROBOT_MOVING) != 0 diff --git a/anki_vector/util.py b/anki_vector/util.py index 98c1b4e..a01f2c2 100644 --- a/anki_vector/util.py +++ b/anki_vector/util.py @@ -405,10 +405,10 @@ def __le__(self, other): @property def abs_value(self): """:class:`anki_vector.util.Angle`: The absolute value of the angle. - + If the Angle is positive then it returns a copy of this Angle, otherwise it returns -Angle. """ - return Angle(radians = abs(self._radians)) + return Angle(radians=abs(self._radians)) def angle_z_to_quaternion(angle_z: Angle): diff --git a/docs/source/api.rst b/docs/source/api.rst index 69e41ed..1602ab9 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -27,6 +27,7 @@ The API anki_vector.proximity anki_vector.robot anki_vector.screen + anki_vector.status anki_vector.touch anki_vector.util anki_vector.viewer From 38753365445f9220d1068b565bd99278e01b9c3d Mon Sep 17 00:00:00 2001 From: Shawn Blakesley Date: Mon, 3 Dec 2018 12:20:37 -0800 Subject: [PATCH 06/13] VIC-11408 Fix 3d Viewer example before making it a separate process (#25) - Added event stream thread - Fixed VIC-9834: robot.accel and robot.gyro always return None on first call. - Fixed VIC-12128: Occasional exception breaking script in clear_when_done - ValueError: list.remove(x): x not in list --- anki_vector/connection.py | 5 +- anki_vector/events.py | 146 +++++++++++++----- anki_vector/exceptions.py | 2 +- anki_vector/opengl_viewer.py | 14 +- anki_vector/proximity.py | 3 +- anki_vector/robot.py | 4 +- anki_vector/touch.py | 11 +- anki_vector/util.py | 15 +- anki_vector/viewer.py | 28 ++-- anki_vector/world.py | 22 ++- examples/apps/3d_viewer/3d_viewer.py | 3 +- .../tutorials/11_face_event_subscription.py | 23 +-- .../tutorials/12_wake_word_subscription.py | 23 +-- 13 files changed, 187 insertions(+), 112 deletions(-) diff --git a/anki_vector/connection.py b/anki_vector/connection.py index 155c688..6671aaa 100644 --- a/anki_vector/connection.py +++ b/anki_vector/connection.py @@ -704,7 +704,7 @@ async def log_handler(conn: Connection, func: Coroutine, logger: logging.Logger, if not conn.requires_behavior_control: raise VectorControlException(func.__name__) logger.info(f"Delaying {func.__name__} until behavior control is granted") - await conn.control_granted_event.wait() + await asyncio.wait([conn.control_granted_event.wait()], timeout=10) logger.debug(f'Outgoing {func.__name__}: {args[1:] if log_messaging else "size = {} bytes".format(sys.getsizeof(args[1:]))}') try: result = await func(*args, **kwargs) @@ -735,7 +735,8 @@ def result(*args: List[Any], **kwargs: Dict[str, Any]) -> Any: self.conn.active_commands.append(future) def clear_when_done(fut): - self.conn.active_commands.remove(fut) + if fut in self.conn.active_commands: + self.conn.active_commands.remove(fut) future.add_done_callback(clear_when_done) if self.force_async: return future diff --git a/anki_vector/events.py b/anki_vector/events.py index 91c8f55..4b51c64 100644 --- a/anki_vector/events.py +++ b/anki_vector/events.py @@ -21,6 +21,7 @@ import asyncio from concurrent.futures import CancelledError from enum import Enum +import threading from typing import Callable import uuid @@ -63,6 +64,29 @@ class Events(Enum): nav_map_update = "nav_map_update" #: Python event containing nav map data. +class _EventCallback: + def __init__(self, callback, on_connection_thread: bool = False): + self._callback = callback + self._on_connection_thread = on_connection_thread + + @property + def on_connection_thread(self): + return self._on_connection_thread + + @property + def callback(self): + return self._callback + + def __eq__(self, other): + other_cb = other + if hasattr(other, "callback"): + other_cb = other.callback + return other_cb == self.callback + + def __hash__(self): + return self._callback.__hash__() + + class EventHandler: """Listen for Vector events.""" @@ -72,7 +96,10 @@ def __init__(self): self._conn_id = None self.listening_for_events = False self.event_future = None + self._thread: threading.Thread = None + self._loop: asyncio.BaseEventLoop = None self.subscribers = {} + self._done_signal: asyncio.Event = None def start(self, connection: Connection): """Start listening for events. Automatically called by the :class:`anki_vector.robot.Robot` class. @@ -82,7 +109,16 @@ def start(self, connection: Connection): """ self._conn = connection self.listening_for_events = True - self.event_future = asyncio.run_coroutine_threadsafe(self._handle_events(), self._conn.loop) + self._thread = threading.Thread(target=self._run_thread, daemon=True, name="Event Stream Handler Thread") + self._thread.start() + + def _run_thread(self): + self._loop = asyncio.new_event_loop() + asyncio.set_event_loop(self._loop) + self._done_signal = asyncio.Event(loop=self._loop) + # create an event stream handler on the connection thread + self.event_future = asyncio.run_coroutine_threadsafe(self._handle_event_stream(), self._conn.loop) + self._loop.run_until_complete(self._done_signal.wait()) def close(self): """Stop listening for events. Automatically called by the :class:`anki_vector.robot.Robot` class. @@ -93,6 +129,32 @@ def close(self): self.event_future.result() except CancelledError: pass + self._loop.call_soon_threadsafe(self._done_signal.set) + + def _notify(self, event_callback, event_name, event_data): + loop = self._loop + thread = self._thread + # For high priority events that shouldn't be blocked by user callbacks + # they will run directly on the connection thread. This should typically + # be used when setting robot properties from events. + if event_callback.on_connection_thread: + loop = self._conn.loop + thread = self._conn.thread + callback = event_callback.callback + if threading.current_thread() is thread: + if asyncio.iscoroutinefunction(callback): + asyncio.ensure_future(callback(event_name, event_data), loop=loop) + elif asyncio.iscoroutine(callback): + asyncio.ensure_future(callback, loop=loop) + else: + loop.call_soon(callback, event_name, event_data) + else: + if asyncio.iscoroutinefunction(callback): + asyncio.run_coroutine_threadsafe(callback(event_name, event_data), loop=loop) + elif asyncio.iscoroutine(callback): + asyncio.run_coroutine_threadsafe(callback, loop=loop) + else: + loop.call_soon_threadsafe(callback, event_name, event_data) async def dispatch_event_by_name(self, event_data, event_name: str = None): """Dispatches event to event listeners by name. @@ -101,8 +163,9 @@ async def dispatch_event_by_name(self, event_data, event_name: str = None): import anki_vector - def event_listener(_, msg): - print(msg) + def event_listener(name, msg): + print(name) # will print 'my_event' + print(msg) # will print 'my_event dispatched' with anki_vector.Robot() as robot: robot.events.subscribe_by_name(event_listener, event_name='my_event') @@ -116,13 +179,8 @@ def event_listener(_, msg): if event_name in self.subscribers.keys(): subscribers = self.subscribers[event_name].copy() - for func in subscribers: - if asyncio.iscoroutinefunction(func): - self._conn.run_soon(func(event_name, event_data)) - elif asyncio.iscoroutine(func): - self._conn.run_soon(func) - else: - func(event_name, event_data) + for callback in subscribers: + self._notify(callback, event_name, event_data) async def dispatch_event(self, event_data, event_type: Events): """Dispatches event to event listeners.""" @@ -147,7 +205,7 @@ def _unpackage_event(self, enum_key: str, event): return event_key, event_data - async def _handle_events(self): + async def _handle_event_stream(self): self._conn_id = bytes(uuid.uuid4().hex, "utf-8") try: req = protocol.EventRequest(connection_id=self._conn_id) @@ -162,14 +220,16 @@ async def _handle_events(self): except CancelledError: self.logger.debug('Event handler task was cancelled. This is expected during disconnection.') - def subscribe_by_name(self, func: Callable, event_name: str = None): + def subscribe_by_name(self, func: Callable, event_name: str = None, **kwargs): """Receive a method call when the specified event occurs. .. testcode:: - def event_listener(robot, msg): - print(msg) - robot.events.unsubscribe_by_name(event_listener, event_name='my_event') + import anki_vector + + def event_listener(name, msg): + print(name) # will print 'my_event' + print(msg) # will print 'my_event dispatched' with anki_vector.Robot() as robot: robot.events.subscribe_by_name(event_listener, event_name='my_event') @@ -180,12 +240,13 @@ def event_listener(robot, msg): """ if not event_name: self.logger.error('Bad event_name in subscribe.') + on_connection_thread = kwargs["on_connection_thread"] if "on_connection_thread" in kwargs else False if event_name not in self.subscribers.keys(): self.subscribers[event_name] = set() - self.subscribers[event_name].add(func) + self.subscribers[event_name].add(_EventCallback(func, on_connection_thread)) - def subscribe(self, func: Callable, event_type: Events = None): + def subscribe(self, func: Callable, event_type: Events = None, **kwargs): """Receive a method call when the specified event occurs. .. testcode:: @@ -193,26 +254,28 @@ def subscribe(self, func: Callable, event_type: Events = None): import anki_vector from anki_vector.events import Events from anki_vector.util import degrees + import functools import threading said_text = False + evt = threading.Event() + + def on_robot_observed_face(robot, event_type, event): + print("Vector sees a face") + global said_text + if not said_text: + said_text = True + robot.say_text("I see a face!") + evt.set() args = anki_vector.util.parse_command_args() with anki_vector.Robot(enable_face_detection=True) as robot: - evt = threading.Event() - - async def on_robot_observed_face(event_type, event): - print("Vector sees a face") - global said_text - if not said_text: - said_text = True - await robot.say_text("I see a face!") - evt.set() # If necessary, move Vector's Head and Lift to make it easy to see his face robot.behavior.set_head_angle(degrees(50.0)) robot.behavior.set_lift_height(0.0) + on_robot_observed_face = functools.partial(on_robot_observed_face, robot) robot.events.subscribe(on_robot_observed_face, Events.robot_observed_face) print("------ waiting for face events, press ctrl+c to exit early ------") @@ -233,16 +296,18 @@ async def on_robot_observed_face(event_type, event): event_name = event_type.value - self.subscribe_by_name(func, event_name) + self.subscribe_by_name(func, event_name, **kwargs) def unsubscribe_by_name(self, func: Callable, event_name: str = None): """Unregister a previously subscribed method from an event. .. testcode:: - def event_listener(robot, msg): - print(msg) - robot.events.unsubscribe_by_name(event_listener, event_name='my_event') + import anki_vector + + def event_listener(name, msg): + print(name) # will print 'my_event' + print(msg) # will print 'my_event dispatched' with anki_vector.Robot() as robot: robot.events.subscribe_by_name(event_listener, event_name='my_event') @@ -274,27 +339,28 @@ def unsubscribe(self, func: Callable, event_type: Events = None): import anki_vector from anki_vector.events import Events from anki_vector.util import degrees + import functools import threading said_text = False + evt = threading.Event() + + def on_robot_observed_face(robot, event_type, event): + print("Vector sees a face") + global said_text + if not said_text: + said_text = True + robot.say_text("I see a face!") + evt.set() args = anki_vector.util.parse_command_args() with anki_vector.Robot(enable_face_detection=True) as robot: - evt = threading.Event() - - async def on_robot_observed_face(event_type, event): - print("Vector sees a face") - global said_text - if not said_text: - said_text = True - await robot.say_text("I see a face!") - evt.set() # If necessary, move Vector's Head and Lift to make it easy to see his face robot.behavior.set_head_angle(degrees(50.0)) robot.behavior.set_lift_height(0.0) - + on_robot_observed_face = functools.partial(on_robot_observed_face, robot) robot.events.subscribe(on_robot_observed_face, Events.robot_observed_face) print("------ waiting for face events, press ctrl+c to exit early ------") diff --git a/anki_vector/exceptions.py b/anki_vector/exceptions.py index 37448a0..bed9463 100644 --- a/anki_vector/exceptions.py +++ b/anki_vector/exceptions.py @@ -137,7 +137,7 @@ class VectorNotReadyException(_VectorGenericException): class VectorControlTimeoutException(_VectorGenericException): """Failed to get control of Vector. -Please verify that Vector is connected to the internet, and consider trying to request a higher control level. +Please verify that Vector is connected to the internet, is on a flat surface, and is fully charged. """ diff --git a/anki_vector/opengl_viewer.py b/anki_vector/opengl_viewer.py index 2203e51..de75c9a 100644 --- a/anki_vector/opengl_viewer.py +++ b/anki_vector/opengl_viewer.py @@ -248,16 +248,16 @@ def update(self, robot: Robot): if (old_intents.left_wheel_speed != input_intents.left_wheel_speed or old_intents.right_wheel_speed != input_intents.right_wheel_speed): - robot.conn.run_soon(robot.motors.set_wheel_motors(input_intents.left_wheel_speed, - input_intents.right_wheel_speed, - input_intents.left_wheel_speed * 4, - input_intents.right_wheel_speed * 4)) + robot.motors.set_wheel_motors(input_intents.left_wheel_speed, + input_intents.right_wheel_speed, + input_intents.left_wheel_speed * 4, + input_intents.right_wheel_speed * 4) if old_intents.lift_speed != input_intents.lift_speed: - robot.conn.run_soon(robot.motors.set_lift_motor(input_intents.lift_speed)) + robot.motors.set_lift_motor(input_intents.lift_speed) if old_intents.head_speed != input_intents.head_speed: - robot.conn.run_soon(robot.motors.set_head_motor(input_intents.head_speed)) + robot.motors.set_head_motor(input_intents.head_speed) #### Private Methods #### @@ -778,6 +778,7 @@ async def my_function(robot): try: robot.conn.run_coroutine(delegate_function(*function_args)) + # @TODO: Unsubscribe and shut down when the delegate function finishes # This became an issue when the concurrent.future changes were added. @@ -817,7 +818,6 @@ def _on_robot_state_update(self, _, msg): # pylint: disable=unused-argument We can safely capture any robot and world state here, and push to OpenGL (main) thread via a thread-safe queue. """ - world_frame = opengl_vector.WorldRenderFrame(self._robot) self._world_frame_queue.append(world_frame) self._view_controller.update(self._robot) diff --git a/anki_vector/proximity.py b/anki_vector/proximity.py index 07f6fdf..b956df4 100755 --- a/anki_vector/proximity.py +++ b/anki_vector/proximity.py @@ -186,7 +186,8 @@ def __init__(self, robot): # Subscribe to a callback that updates the robot's local properties - which includes proximity data. self._robot.events.subscribe(self._on_robot_state, - Events.robot_state) + Events.robot_state, + on_connection_thread=True) def close(self): """Closing the touch component will unsubscribe from robot state updates.""" diff --git a/anki_vector/robot.py b/anki_vector/robot.py index 2095675..49063ec 100755 --- a/anki_vector/robot.py +++ b/anki_vector/robot.py @@ -688,7 +688,9 @@ def connect(self, timeout: int = 10) -> None: object_detection.result() # Subscribe to a callback that updates the robot's local properties - self.events.subscribe(self._unpack_robot_state, events.Events.robot_state) + self.events.subscribe(self._unpack_robot_state, + events.Events.robot_state, + on_connection_thread=True) def disconnect(self) -> None: """Close the connection with Vector. diff --git a/anki_vector/touch.py b/anki_vector/touch.py index a59b31b..d5bfca5 100755 --- a/anki_vector/touch.py +++ b/anki_vector/touch.py @@ -93,15 +93,14 @@ def __init__(self, robot): self._last_sensor_reading = None # Subscribe to a callback that updates the robot's local properties - which includes touch data. - self._robot.events.subscribe( - self._on_robot_state, - Events.robot_state) + self._robot.events.subscribe(self._on_robot_state, + Events.robot_state, + on_connection_thread=True) def close(self): """Closing the touch component will unsubscribe from robot state updates.""" - self._robot.events.unsubscribe( - self._on_robot_state, - Events.robot_state) + self._robot.events.unsubscribe(self._on_robot_state, + Events.robot_state) @property def last_sensor_reading(self) -> TouchSensorData: diff --git a/anki_vector/util.py b/anki_vector/util.py index a01f2c2..57a6338 100644 --- a/anki_vector/util.py +++ b/anki_vector/util.py @@ -31,6 +31,7 @@ 'Vector2', 'Vector3', 'angle_z_to_quaternion', + 'block_while_none', 'degrees', 'distance_mm', 'distance_inches', @@ -67,7 +68,7 @@ def parse_command_args(parser: argparse.ArgumentParser = None): is specified, we next attempt to read the robot serial number from environment variable ANKI_ROBOT_SERIAL. If ANKI_ROBOT_SERIAL is specified, the value will be used as the robot's serial number. - ..code-block :: + .. code-block:: python import anki_vector @@ -90,16 +91,8 @@ def parse_command_args(parser: argparse.ArgumentParser = None): def block_while_none(interval: float = 0.1, max_iterations: int = 50): """Use this to denote a property that may need some delay before it appears. - ..code-block :: - - class TestClass: - def __init__(self): - self._thing = None - - @property - @block_while_none(interval=0.5, max_iterations=10) - def thing(self): - return self._thing + :param interval: how often to check if the property is no longer None + :param max_iterations: how many times to check the property before raising an error """ def blocker(func: Callable): @wraps(func) diff --git a/anki_vector/viewer.py b/anki_vector/viewer.py index 40735b7..5fe437e 100644 --- a/anki_vector/viewer.py +++ b/anki_vector/viewer.py @@ -18,7 +18,6 @@ # __all__ should order by constants, event classes, other classes, functions. __all__ = ['ViewerComponent'] -import asyncio import multiprocessing as mp import sys @@ -62,8 +61,8 @@ class ViewerComponent(util.Component): def __init__(self, robot): super().__init__(robot) self.overlays: list = [] + self._close_event: mp.Event = None self._frame_queue: mp.Queue = None - self._loop: asyncio.BaseEventLoop = None self._process = None def show_video(self, timeout: float = 10.0) -> None: @@ -85,8 +84,9 @@ def show_video(self, timeout: float = 10.0) -> None: :param timeout: Render video for the given time. (Renders forever, if timeout not given) """ ctx = mp.get_context('spawn') + self._close_event = ctx.Event() self._frame_queue = ctx.Queue(maxsize=4) - self._process = ctx.Process(target=ViewerComponent._render_frames, args=(self._frame_queue, self.overlays, timeout), daemon=True) + self._process = ctx.Process(target=ViewerComponent._render_frames, args=(self._frame_queue, self._close_event, self.overlays, timeout), daemon=True) self._process.start() def stop_video(self) -> None: @@ -101,8 +101,14 @@ def stop_video(self) -> None: time.sleep(10) robot.viewer.stop_video() """ + if self._close_event: + self._close_event.set() + self._close_event = None if self._frame_queue: - self._frame_queue.put(None, False) + try: + self._frame_queue.put(None, False) + except mp.queues.Full: + pass self._frame_queue = None if self._process: self._process.join(timeout=5) @@ -128,7 +134,8 @@ def enqueue_frame(self, image: Image.Image): :param image: A frame from Vector's camera. """ - if self._frame_queue is not None: + close_event = self._close_event + if self._frame_queue is not None and close_event is not None and not close_event.is_set(): try: self._frame_queue.put(image, False) except mp.queues.Full: @@ -141,7 +148,7 @@ def _apply_overlays(self, image: Image.Image) -> None: return image @staticmethod - def _render_frames(queue: mp.Queue, overlays: list = None, timeout: float = 10.0) -> None: + def _render_frames(queue: mp.Queue, event: mp.Event, overlays: list = None, timeout: float = 10.0) -> None: """Rendering the frames in another process. This allows the UI to have the main thread of its process while the user code continues to execute. @@ -154,6 +161,8 @@ def _render_frames(queue: mp.Queue, overlays: list = None, timeout: float = 10.0 try: image = queue.get(True, timeout=timeout) while image: + if event.is_set(): + break if overlays: for overlay in overlays: overlay.apply_overlay(image) @@ -167,6 +176,7 @@ def _render_frames(queue: mp.Queue, overlays: list = None, timeout: float = 10.0 pass except KeyboardInterrupt: pass - - cv2.destroyWindow(window_name) - cv2.waitKey(1) + finally: + event.set() + cv2.destroyWindow(window_name) + cv2.waitKey(1) diff --git a/anki_vector/world.py b/anki_vector/world.py index de75bd4..fa8a470 100644 --- a/anki_vector/world.py +++ b/anki_vector/world.py @@ -75,13 +75,13 @@ def __init__(self, robot): self._objects = {} # Subscribe to callbacks that updates the world view - self._robot.events.subscribe( - self._on_face_observed, - Events.robot_observed_face) + self._robot.events.subscribe(self._on_face_observed, + Events.robot_observed_face, + on_connection_thread=True) - self._robot.events.subscribe( - self._on_object_observed, - Events.robot_observed_object) + self._robot.events.subscribe(self._on_object_observed, + Events.robot_observed_object, + on_connection_thread=True) #### Public Properties #### @@ -245,13 +245,11 @@ def close(self): for obj in self._objects.values(): obj.teardown() - self._robot.events.unsubscribe( - self._on_face_observed, - Events.robot_observed_face) + self._robot.events.unsubscribe(self._on_face_observed, + Events.robot_observed_face) - self._robot.events.unsubscribe( - self._on_object_observed, - Events.robot_observed_object) + self._robot.events.unsubscribe(self._on_object_observed, + Events.robot_observed_object) def get_object(self, object_id: int): """Fetches an object instance with the given id. diff --git a/examples/apps/3d_viewer/3d_viewer.py b/examples/apps/3d_viewer/3d_viewer.py index 6752867..ebed01f 100755 --- a/examples/apps/3d_viewer/3d_viewer.py +++ b/examples/apps/3d_viewer/3d_viewer.py @@ -27,8 +27,7 @@ async def my_function(): - await asyncio.sleep(20) - print("done") + pass def main(): diff --git a/examples/tutorials/11_face_event_subscription.py b/examples/tutorials/11_face_event_subscription.py index d20018f..d90d2bb 100755 --- a/examples/tutorials/11_face_event_subscription.py +++ b/examples/tutorials/11_face_event_subscription.py @@ -23,6 +23,7 @@ he finishes speaking. """ +import functools import threading import anki_vector @@ -33,22 +34,24 @@ def main(): + evt = threading.Event() + + def on_robot_observed_face(robot, event_type, event): + print("Vector sees a face") + global said_text + if not said_text: + said_text = True + robot.say_text("I see a face!") + evt.set() + args = anki_vector.util.parse_command_args() with anki_vector.Robot(args.serial, enable_face_detection=True) as robot: - evt = threading.Event() - - async def on_robot_observed_face(event_type, event): - print("Vector sees a face") - global said_text - if not said_text: - said_text = True - await robot.say_text("I see a face!") - evt.set() # If necessary, move Vector's Head and Lift to make it easy to see his face robot.behavior.set_head_angle(degrees(50.0)) robot.behavior.set_lift_height(0.0) + on_robot_observed_face = functools.partial(on_robot_observed_face, robot) robot.events.subscribe(on_robot_observed_face, Events.robot_observed_face) print("------ waiting for face events, press ctrl+c to exit early ------") @@ -59,6 +62,8 @@ async def on_robot_observed_face(event_type, event): except KeyboardInterrupt: pass + robot.events.unsubscribe(on_robot_observed_face, Events.robot_observed_face) + if __name__ == '__main__': main() diff --git a/examples/tutorials/12_wake_word_subscription.py b/examples/tutorials/12_wake_word_subscription.py index bdf3525..86b0565 100755 --- a/examples/tutorials/12_wake_word_subscription.py +++ b/examples/tutorials/12_wake_word_subscription.py @@ -23,7 +23,7 @@ connection.py for more information. """ - +import functools import threading import anki_vector @@ -33,19 +33,20 @@ def main(): - args = anki_vector.util.parse_command_args() - with anki_vector.Robot(args.serial, requires_behavior_control=False, cache_animation_list=False) as robot: - evt = threading.Event() + evt = threading.Event() - async def on_wake_word(event_type, event): - await robot.conn.request_control() + def on_wake_word(robot, event_type, event): + robot.conn.request_control() - global wake_word_heard - if not wake_word_heard: - wake_word_heard = True - await robot.say_text("Hello") - evt.set() + global wake_word_heard + if not wake_word_heard: + wake_word_heard = True + robot.say_text("Hello") + evt.set() + args = anki_vector.util.parse_command_args() + with anki_vector.Robot(args.serial, requires_behavior_control=False, cache_animation_list=False) as robot: + on_wake_word = functools.partial(on_wake_word, robot) robot.events.subscribe(on_wake_word, Events.wake_word) print('------ Vector is waiting to hear "Hey Vector!" Press ctrl+c to exit early ------') From 177ac52b8de3dc854233c886be01628c33c5bddf Mon Sep 17 00:00:00 2001 From: Shawn Blakesley Date: Mon, 3 Dec 2018 14:27:16 -0800 Subject: [PATCH 07/13] VIC-12181 Update pypi package copy (#31) --- setup.py | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/setup.py b/setup.py index 8d3e3bc..f949bcd 100644 --- a/setup.py +++ b/setup.py @@ -3,39 +3,17 @@ """ Vector SDK, by Anki. -Vector is the home robot who hangs out and helps out. +The Vector SDK is a connected vision- and character-based robotics platform for everyone. -This library lets you take command of Vector and write programs for him. +The Vector SDK gives you direct access to Vector's unprecedented set of advanced sensors, AI capabilities, and robotics technologies including computer vision, intelligent mapping and navigation, and a groundbreaking collection of expressive animations. -Vector features: +It's powerful but easy to use, complex but not complicated, and versatile enough to be used across a wide range of domains including enterprise, research, and entertainment. Find out more at https://developer.anki.com - * A camera with advanced vision system - * A robotic lifter - * Independent tank treads - * Pivotable head - * An accelerometer - * A gyroscope - * Cliff detection - * Face recognition - * Path planning - * Animation and behavior systems - * Light cube, with LEDs, an accelerometer and tap detection - * Single point time-of-flight NIR Laser - * Capactive casing - -This SDK provides users with access to take control of Vector and write simple -or advanced programs with him. +Vector SDK documentation: https://developer.anki.com/vector/docs/ +Official developer forum: https://forums.anki.com/ Requirements: * Python 3.6.1 or later - -Optional requirements for camera image processing/display: - * Pillow - * NumPy - -Optional requirements for 3D viewer/visualization: - * PyOpenGL - * Pillow """ import os.path @@ -43,7 +21,7 @@ from setuptools import setup if sys.version_info < (3, 6, 1): - sys.exit('The Anki Vector SDK requires Python 3.6.1 or later') + sys.exit('The Vector SDK requires Python 3.6.1 or later') HERE = os.path.abspath(os.path.dirname(__file__)) @@ -68,7 +46,7 @@ def get_requirements(): setup( name='anki_vector', version=VERSION, - description="SDK for Anki's Vector robot, the home robot who hangs out and helps out", + description="The Vector SDK is a connected vision- and character-based robotics platform for everyone.", long_description=__doc__, url='https://developer.anki.com', author='Anki, Inc', From 3e93afaf4723271a314a81ca259580ceee7314c6 Mon Sep 17 00:00:00 2001 From: Shawn Blakesley Date: Mon, 3 Dec 2018 17:13:50 -0800 Subject: [PATCH 08/13] Move configure.py into anki_vector.configure (#33) --- configure.py => anki_vector/configure/__main__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename configure.py => anki_vector/configure/__main__.py (100%) diff --git a/configure.py b/anki_vector/configure/__main__.py similarity index 100% rename from configure.py rename to anki_vector/configure/__main__.py From 221ed1dfda1d934b0b6acdf4117f5edf80550a9a Mon Sep 17 00:00:00 2001 From: Michelle Sintov Date: Mon, 3 Dec 2018 22:18:27 -0800 Subject: [PATCH 09/13] Update docs for alpha release: add anki_vector.configure refs, PyPI anki_vector, etc. #32 --- README.md | 4 +-- anki_vector/opengl.py | 6 ++--- anki_vector/opengl_vector.py | 2 -- anki_vector/robot.py | 4 +-- anki_vector/version.py | 2 +- docs/source/advanced-tips.rst | 10 ++++---- docs/source/downloads.rst | 25 +++++++++++++++++++ docs/source/getstarted.rst | 2 +- docs/source/index.rst | 6 +++++ docs/source/install-linux.rst | 16 +++++------- docs/source/install-macos.rst | 8 +++--- docs/source/install-windows.rst | 8 +++--- docs/source/troubleshooting.rst | 12 ++++----- .../apps/remote_control/remote_control.py | 11 +++----- 14 files changed, 67 insertions(+), 49 deletions(-) create mode 100644 docs/source/downloads.rst diff --git a/README.md b/README.md index 6ef477b..cd655ae 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Before connecting, you will need: * Vector's IP Address: The ip address can be found by first placing Vector on the charger, then double-clicking the button on his back, and finally raising and lowering his arms. It is possible for your ip to change based on your network settings, so it must be updated accordingly. Example: `192.168.43.48` * Vector's Serial Number: You may find this number on the underside of your robot. Example: `00e20115` -These will be needed to run the configure.py script and set up authentication from your device to your Vector. +These will be needed to run the `anki_vector.configure` executable submodule and set up authentication from your device to your Vector. Your device must have Python 3.6.1 or later installed. Please see the documentation pages mentioned below for instructions to install Python. @@ -19,7 +19,7 @@ Check out the documentation for setup instructions by opening docs/build/html/in --- -During setup, you will configure your `anki_vector` SDK authentication from a terminal using `configure.py`. +During setup, you will configure your `anki_vector` SDK authentication from a terminal using `'python3 -m anki_vector.configure`. By running this script, you will be asked to provide your Anki account credentials, and the script will download an authentication token and cert that will grant you access to the robot and his capabilities (such as camera and audio) as well as data stored on the robot (such as faces and photos). diff --git a/anki_vector/opengl.py b/anki_vector/opengl.py index df885d5..ac0b5c2 100644 --- a/anki_vector/opengl.py +++ b/anki_vector/opengl.py @@ -21,7 +21,9 @@ This package requires Python to have the PyOpenGL package installed, along with an implementation of GLUT (OpenGL Utility Toolkit). - To install the Python packages do ``pip install .[3dviewer]`` + To install the Python packages on Mac and Linux do ``python3 -m pip install --user "anki_vector[3dviewer]"`` + + To install the Python packages on Windows do ``py -3 -m pip install --user "anki_vector[3dviewer]"`` On Windows and Linux you must also install freeglut (macOS / OSX has one preinstalled). @@ -34,8 +36,6 @@ script to use it." """ -# TODO Update install line above to: ``pip3 install --user "anki_vector[3dviewer]"`` - # __all__ should order by constants, event classes, other classes, functions. __all__ = ['Camera', 'DynamicTexture', 'MaterialLibrary', 'MeshData', 'MeshFace', 'MeshGroup', 'OpenGLWindow', 'PrecomputedView', 'ResourceManager', diff --git a/anki_vector/opengl_vector.py b/anki_vector/opengl_vector.py index 8875a1b..72e5359 100644 --- a/anki_vector/opengl_vector.py +++ b/anki_vector/opengl_vector.py @@ -15,8 +15,6 @@ """This module provides Vector-specific 3D support classes for OpenGL, used by opengl_viewer.py. """ -# TODO Update install line above to: ``pip3 install --user "anki_vector[3dviewer]"`` - # __all__ should order by constants, event classes, other classes, functions. __all__ = ['CubeRenderFrame', 'FaceRenderFrame', 'LightCubeView', 'RobotRenderFrame', 'RobotView', 'UnitCubeView', 'VectorViewManifest', 'WorldRenderFrame'] diff --git a/anki_vector/robot.py b/anki_vector/robot.py index 49063ec..28a19e8 100755 --- a/anki_vector/robot.py +++ b/anki_vector/robot.py @@ -118,7 +118,7 @@ def __init__(self, self._port = config["port"] if self._name is None or self._ip is None or self._cert_file is None or self._guid is None: - raise ValueError("The Robot object requires a serial and for Vector to be logged in (using the app then configure.py).\n" + raise ValueError("The Robot object requires a serial and for Vector to be logged in (using the app then running the anki_vector.configure executable submodule).\n" "You may also provide the values necessary for connection through the config parameter. ex: " '{"name":"Vector-XXXX", "ip":"XX.XX.XX.XX", "cert":"/path/to/cert_file", "guid":""}') @@ -181,7 +181,7 @@ def _read_configuration(self, serial: str) -> dict: sections = parser.sections() if not sections: - raise Exception('\n\nCould not find the sdk configuration file. Please run ./configure.py to set up your Vector for SDK usage.') + raise Exception('\n\nCould not find the sdk configuration file. Please run `python3 -m anki_vector.configure` to set up your Vector for SDK usage.') elif serial is None and len(sections) == 1: serial = sections[0] self.logger.warning("No serial number provided. Automatically selecting {}".format(serial)) diff --git a/anki_vector/version.py b/anki_vector/version.py index dc39182..89b7528 100644 --- a/anki_vector/version.py +++ b/anki_vector/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.5.0" +__version__ = "0.5.1" diff --git a/docs/source/advanced-tips.rst b/docs/source/advanced-tips.rst index 30ad600..54e434b 100644 --- a/docs/source/advanced-tips.rst +++ b/docs/source/advanced-tips.rst @@ -12,14 +12,14 @@ Moving Vector between WiFi networks When you move Vector from one WiFi network to another (using the Vector App), or if your Vector's IP changes, you will also need to make some changes to -your SDK setup. To assist in this migration, ``configure.py`` provides a ``-u`` -parameter to quickly reconnect to Vector. +your SDK setup. To assist in this migration, the ``anki_vector.configure`` +executable submodule provides a ``-u`` parameter to quickly reconnect to Vector. To update your connection, you will need to find the IP address on Vector's face, and the serial number of the robot you are updating. Then from your terminal run:: - python3 configure.py -u "" -s "" + python3 -m anki_vector.configure -u "" -s "" ^^^^^^^^^^^^^^^^^^^^^^ @@ -57,9 +57,9 @@ and set it to Vector's serial number:: Set ANKI_ROBOT_HOST and VECTOR_ROBOT_NAME ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When running ``configure.py``, you must provide Vector's ip and name. +When running the ``anki_vector.configure`` executable submodule, you must provide Vector's ip and name. To avoid typing these in, you can instead create environment variables -ANKI_ROBOT_HOST and VECTOR_ROBOT_NAME. Then ``configure.py`` will automatically pick +ANKI_ROBOT_HOST and VECTOR_ROBOT_NAME. Then ``anki_vector.configure`` will automatically pick up those settings:: export ANKI_ROBOT_HOST="192.168.42.42" diff --git a/docs/source/downloads.rst b/docs/source/downloads.rst new file mode 100644 index 0000000..932d08b --- /dev/null +++ b/docs/source/downloads.rst @@ -0,0 +1,25 @@ +######### +Downloads +######### + +------------ +SDK Examples +------------ + +Download Python example scripts that use the Vector SDK. + +:verlink:`macOS/Linux SDK Examples ` + +:verlink:`Windows SDK Examples ` + +------ +GitHub +------ + +Clone, fork, or report issues on the `GitHub vector-python-sdk repository `_. + +---- + +`Terms and Conditions `_ and `Privacy Policy `_ + +`Click here to return to the Anki Developer website. `_ diff --git a/docs/source/getstarted.rst b/docs/source/getstarted.rst index 71ea479..ea7e500 100644 --- a/docs/source/getstarted.rst +++ b/docs/source/getstarted.rst @@ -34,7 +34,7 @@ Prerequisites Starting Up the SDK ------------------- -On the computer, open Terminal (macOS/Linux) or Command Prompt (Windows) and navigate into your ``vector_python_sdk_0.5.0`` folder by typing ``cd vector_python_sdk_0.5.0``, and press **Enter**. +On the computer, open Terminal (macOS/Linux) or Command Prompt (Windows) and type ``cd vector_sdk_examples``, where *vector_sdk_examples* is the directory you extracted the examples into, and press **Enter**. ---------------- Example Program diff --git a/docs/source/index.rst b/docs/source/index.rst index a4b7d77..ba85fd3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -17,6 +17,12 @@ Welcome to the Vector SDK! troubleshooting.rst advanced-tips.rst +.. toctree:: + :maxdepth: 2 + :caption: Downloads + + downloads.rst + .. toctree:: :maxdepth: 2 :caption: Get Started diff --git a/docs/source/install-linux.rst b/docs/source/install-linux.rst index 176bb2b..f31f539 100644 --- a/docs/source/install-linux.rst +++ b/docs/source/install-linux.rst @@ -48,8 +48,7 @@ SDK Installation To install the SDK, type the following into the Terminal window:: - cd vector_python_sdk_0.5.0 - python3 -m pip install . + python3 -m pip install --user anki_vector ^^^^^^^^^^^^^^^^^^^^^ Vector Authentication @@ -57,14 +56,13 @@ Vector Authentication To authenticate with the robot, type the following into the Terminal window:: - cd vector_python_sdk_0.5.0 - ./configure.py + python3 -m anki_vector.configure You will be prompted for your robot's name, ip address and serial number. You will also be asked for your Anki login and password. Make sure to use the same account that was used to set up your Vector. You will see "SUCCESS!" when this script successfully completes. -.. note:: Running ``configure.py`` will automatically download the Vector robot certificate to your computer and store credentials to allow you to connect to Vector. These credentials will be stored under your home directory in folder ``.anki_vector``. +.. note:: Running ``anki_vector.configure`` executable submodule will automatically download the Vector robot certificate to your computer and store credentials to allow you to connect to Vector. These credentials will be stored under your home directory in folder ``.anki_vector``. .. warning:: These credentials give full access to your robot, including camera stream, audio stream and data. Do not share these credentials. @@ -99,8 +97,7 @@ SDK Installation To install the SDK, type the following into the Terminal window:: - cd vector_python_sdk_0.5.0 - python3.6 -m pip install . + python3.6 -m pip install --user anki_vector ^^^^^^^^^^^^^^^^^^^^^ Vector Authentication @@ -108,14 +105,13 @@ Vector Authentication To authenticate with the robot, type the following into the Terminal window:: - cd vector_python_sdk_0.5.0 - python3.6 configure.py + python3.6 -m anki_vector.configure You will be prompted for your robot's name, ip address and serial number. You will also be asked for your Anki login and password. Make sure to use the same account that was used to set up your Vector. You will see "SUCCESS!" when this script successfully completes. -.. note:: Running ``configure.py`` will automatically download the Vector robot certificate to your computer and store credentials to allow you to connect to Vector. These credentials will be stored under your home directory in folder ``.anki_vector``. +.. note:: Running ``anki_vector.configure`` executable submodule will automatically download the Vector robot certificate to your computer and store credentials to allow you to connect to Vector. These credentials will be stored under your home directory in folder ``.anki_vector``. .. warning:: These credentials give full access to your robot, including camera stream, audio stream and data. Do not share these credentials. diff --git a/docs/source/install-macos.rst b/docs/source/install-macos.rst index 6a590d7..e8bd5b4 100644 --- a/docs/source/install-macos.rst +++ b/docs/source/install-macos.rst @@ -39,8 +39,7 @@ SDK Installation To install the SDK, navigate into the SDK folder and type the following into a Terminal window:: - cd vector_python_sdk_0.5.0 - python3 -m pip install . + python3 -m pip install --user anki_vector ^^^^^^^^^^^^^^^^^^^^^ Vector Authentication @@ -48,14 +47,13 @@ Vector Authentication To authenticate with the robot, type the following into the Terminal window:: - cd vector_python_sdk_0.5.0 - ./configure.py + python3 -m anki_vector.configure You will be prompted for your robot's name, ip address and serial number. You will also be asked for your Anki login and password. Make sure to use the same account that was used to set up your Vector. You will see "SUCCESS!" when this script successfully completes. -.. note:: Running ``configure.py`` will automatically download the Vector robot certificate to your computer and store credentials to allow you to connect to Vector. These credentials will be stored under your home directory in folder ``.anki_vector``. +.. note:: Running the ``anki_vector.configure`` executable submodule will automatically download the Vector robot certificate to your computer and store credentials to allow you to connect to Vector. These credentials will be stored under your home directory in folder ``.anki_vector``. .. warning:: These credentials give full access to your robot, including camera stream, audio stream and data. Do not share these credentials. diff --git a/docs/source/install-windows.rst b/docs/source/install-windows.rst index a86b5dc..1601e4c 100644 --- a/docs/source/install-windows.rst +++ b/docs/source/install-windows.rst @@ -33,8 +33,7 @@ SDK Installation To install the SDK, navigate into the SDK folder and type the following into a Command Prompt window:: - cd vector_python_sdk_0.5.0 - py -3 -m pip install . + py -3 -m pip install --user anki_vector .. note:: If you encounter an error during SDK installation, you may need to upgrade your pip install. Try ``python -m pip install --upgrade pip`` or ``py -3 -m pip install --upgrade pip`` @@ -46,14 +45,13 @@ Vector Authentication To authenticate with the robot, type the following into the Terminal window:: - cd vector_python_sdk_0.5.0 - py configure.py + py -m anki_vector.configure You will be prompted for your robot's name, ip address and serial number. You will also be asked for your Anki login and password. Make sure to use the same account that was used to set up your Vector. You will see "SUCCESS!" when this script successfully completes. -.. note:: Running ``configure.py`` will automatically download the Vector robot certificate to your computer and store credentials to allow you to connect to Vector. These credentials will be stored under your home directory in folder ``.anki_vector``. +.. note:: Running the ``anki_vector.configure`` executable submodule will automatically download the Vector robot certificate to your computer and store credentials to allow you to connect to Vector. These credentials will be stored under your home directory in folder ``.anki_vector``. .. warning:: These credentials give full access to your robot, including camera stream, audio stream and data. Do not share these credentials. diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index adeba99..4b9d137 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -36,20 +36,20 @@ Tutorial program does not run Before running a Python program, be sure you can see Vector's eyes. If instead you see an image of a mobile device, the Customer Care Info screen, a missing Wifi icon, or something else, please complete setup of your Vector first and then you will be ready set up the SDK. -Also, check whether Vector's IP address has changed since the last time you ran ``configure.py``. If so, see :ref:`moving_between_wifi` to set up the robot with the new IP address. +Also, check whether Vector's IP address has changed since the last time you ran ``anki_vector.configure``. If so, see :ref:`moving_between_wifi` to set up the robot with the new IP address. -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Failure to run configure.py -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Failure to run anki_vector.configure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The prerequisites to run ``configure.py`` are: +The prerequisites to run the ``anki_vector.configure`` executable submodule successfully are: -* Vector is powered on. * You have successfully created an Anki account. * Vector has been set up with the Vector companion app. * The Vector companion app is *not* currently connected to Vector. * Vector is connected to the same network as your computer. +* The ``anki_vector`` Python package must be installed. * You can see Vector's eyes on his screen. diff --git a/examples/apps/remote_control/remote_control.py b/examples/apps/remote_control/remote_control.py index 8f3b688..a98adc8 100755 --- a/examples/apps/remote_control/remote_control.py +++ b/examples/apps/remote_control/remote_control.py @@ -25,6 +25,10 @@ import time from lib import flask_helpers +import anki_vector +from anki_vector import util + + try: from flask import Flask, request except ImportError: @@ -35,13 +39,6 @@ except ImportError: sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install") -try: - import anki_vector - from anki_vector import util -except ImportError: - sys.exit("Cannot import anki_vector: Do `pip3 install -e .` in the vector home folder to install") - - def create_default_image(image_width, image_height, do_gradient=False): """Create a place-holder PIL image to use until we have a live feed from Vector""" image_bytes = bytearray([0x70, 0x70, 0x70]) * image_width * image_height From 521a0ff1d001fbb69a5245ba70d764079747fb95 Mon Sep 17 00:00:00 2001 From: Michelle Sintov Date: Tue, 4 Dec 2018 09:44:54 -0800 Subject: [PATCH 10/13] Update index with image and text from Kaiser #34 --- docs/source/getstarted.rst | 8 ++++---- docs/source/images/vector-sdk-alpha.jpg | Bin 0 -> 47066 bytes docs/source/images/vector-sdk-img.png | Bin 73826 -> 0 bytes docs/source/index.rst | 14 ++++++++++---- docs/source/initial.rst | 8 ++++---- docs/source/install-linux.rst | 2 +- docs/source/install-macos.rst | 2 +- docs/source/install-windows.rst | 2 +- docs/source/troubleshooting.rst | 8 ++++---- 9 files changed, 25 insertions(+), 19 deletions(-) create mode 100644 docs/source/images/vector-sdk-alpha.jpg delete mode 100644 docs/source/images/vector-sdk-img.png diff --git a/docs/source/getstarted.rst b/docs/source/getstarted.rst index ea7e500..e201307 100644 --- a/docs/source/getstarted.rst +++ b/docs/source/getstarted.rst @@ -4,11 +4,11 @@ Getting Started With the Vector SDK To make sure you get the best experience possible out of the SDK, please ensure you have followed the steps in the :doc:`Initial Setup `. ------------------ -Anki SDK Forums ------------------ +--------------------- +Anki Developer Forums +--------------------- -Please visit our `Anki SDK Forums `_ where you can: +Please visit our `Anki Developer Forums `_ where you can: * Get assistance with your code diff --git a/docs/source/images/vector-sdk-alpha.jpg b/docs/source/images/vector-sdk-alpha.jpg new file mode 100644 index 0000000000000000000000000000000000000000..91c538977c7f8fd34ce66fd4f2b51f642aec61cc GIT binary patch literal 47066 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 O95sL9SDuf#C;tLBN#Io000U@X+uL$Nkc;* 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%0D{x3Wp*zwl}am2Ml4g2 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 JZ@09RR2rn5HFxgLiy2iCiU;oRmoe?TwPY0EP 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 z zxN+c(>u$LF-oGlj9y`WHo&!OI8`GJ(22&tRrE6hxaM9Yyv?(nt74P}yX6rXdVy6)E z76}^?$Ak|-Ydwa&=$MAiIH~bPTH9VA1Q1eP>d>+w!wSKAL=b9wB2y1akdma-A)tyf z02VO@GZ_MsAi!~~D8iIYR1%4^Qq;A~8e9r&k1eSD%}x$lHsq=;P;`+1oTp&97W7~) zhl^R8`59rz-)nQiC9c)^eC_Y!6Q6UWG53iqfkwuJy`LB*2_g67I5scimPmxc3urkS zw}9K5?b|zYknJ1DuV63PzRcaut0kM3d-eoL~Xl(g`RT{#T5yihG%%EUC58{ZK z6AallfqZ%Zjx=UmC1EShinCcZx1g8hnayktViqhAzDTp%i3P=*r!SCh14yGV7KV%6 zm1US5GvWA#?koB#LtnV>OFtd`|5tV2%BCed#g7l)S#`8_p6$NU|q2UP20w zpj!A_AD5~{O>Lxw{qzUUeisMXS;6u^GAT<*7>7Q8_`H{2=iA#ErwE=*RcAH#p^*)_ zIXzKB2nZy_SK_CXXjKcQDL^0~Awd{w1E);TfGxrpK#MZRA*``DqJH0(z4Ko|gBcpu zc_fZQfFtf!V4riK0gizWNGL^Oh2cTmVu(y6zdX-vT0q1A?7BzwON?0r=}j*G4t3^_MA;USH9!CTc)Gw z4aFC%>sr#6@&5DRSM7caq(t;g+wfGY!K6YIDpqMKZ;{C60CgyAFEv5>gAzwd*GuGL zEt5A!3en}UROd_E8P5egsaTUKB?zdy-4P8?=;n8XDqe${9t4$2~2YT!a``e zT;>N@F0dTRya620$Op_+)TeM`#i-S4U64Us? z?kkq%S8tvE#c!>D`^J@BaTVP3<3H|l3s+bdJrF$(b%W7#*Z^DFj;p80SO%y~0g&j6 z=_96k0RUByD)*yAEK5E&4v0Qd8JLb~=#1wEp43&0?eqtr!w#g=44{er&@T6o3N)zY zQ9NTndT6`6(3_bCXvHH>18xCol+R|D##S&n_4IfCCf_|!%;g=|@*GE)rWiuS)IyT^ zB&d*ll4Hy_1ePhW5uF2}X+NLWTtO*Ok9azO2TJO{C#SaTtUO+HdQ0vfj2#7UyZh~{ za_1v<`tfnnLlCtzWRQjw)0?R-gc^k~7^t=fDu#fP@ePuKR+-3XK1^d3TFAy2qK;|k zjOWgMNW+sFNHpXnF*!jJR)}wVGdu}Zuq+9e0wHKrCK0m^TnApiiv<_N;nc*iYL^CZ zZIX!hU`{~bcM{jjk(YI-g>l5!cs`k9u@v)ss1#*lMnP!hqckN^kS%2cF5w!eOeu}x zNEiYxT7f}Kxj|yah-6T9+&{vO=+7>DWc<)QBj4+F2U#TI0EdQ{&rJeNIFL$CG%4Gu zbsnU85gOk?+JkiWQT;I)>W9itC?b`{B9sYHRsy91n9k^o=PG}p7=1GPuX;nt{!sFn zbjgWGx5)#D{z-sblhs3Tnfcs~0CG|*IbyzpQgw4OG!6lY2#zQT<>_!7R;G_qnb*`4 zj^{iD-I>b#r@UKrQ$TWxlm(N45!AmBzcnD=C~L#iKFT}}OZq;2?$FMeM;;vgVa~}r z&|OwojioJX3K@(eV3aupj7arFr%JR}OnytW7?fmkj-5(zUQUbB?ZWsTq$D}D=OD^L z)KkIv=I`b2<2C7>?`LP6KJcWJoloFNsq!=zJSi7NLLdMscv74ttXla25>~Y{jzl7p zCn7kb0U{a&yzs%!a%V#%DExZaDRwE#WWpOF=Qh>i_>`vbKNVO&9WF==z_MJ=4tyfk zaU_izdnIG%g~g$m;*6EwJo4Rv?20b8??t_rZ0z0e(A0lHN)c%410oH93KjrLO4Y=o zZaRXQKtSY7lo=7iShD6z0}jH05XM3ur?f2ezeXlp{1G~)p)*cfc+yykIp9g2s>HIM z!oZV4`+#rY$_W3Odqv?l*wBMKZAn5qSF#IzPPV`q4Z##&%(*cjap2=zfnSWX7BCd# zOI`}bIS@pl|H=p8ghCPBC|}2z4c;O%jAdPeC{Y(j3*V;8?yJVroaTg_qSJl2wr5k{ zm0x=1qfh=?`To)SLpv6#aJr<6|Ar>vr~?IM=a7aQQ{V!38<1A~Rv`ltikKQnKuz*k zP-|%!L_&oTvIhXaX2&#i#_0-Ani>oZ&bPsn(pcz(g_u!KldO?*&81@rN1CS{+ z0cbrukmz7TT~>imvL7YhflOdZcvad9#iWF(6R95^)6f~GBRrWxC8>!obQKm+<(-Pi zgJdirJqb~0A%YRY3m8aHMwt>4p+1W#_$0^7D8#YfyJU4^;$RfTY%+z7TA0ZJ z&R>#F0nbvhlf)QfpHSJQLOKq@ItUV>uo6W6m()#D<>qK05g<&k*iTDN@1%dUKeOzv zkvj@jPu>B;^~05HBSmaBJwN0}fIv$6(jbw#)rTsSkS`2=9hF8cTDlF(ny07h<197VPkHI9;~`WI#{>G$LL{2~!Qf%*u{X zY7Wc38lo5^_26tUh!T`hs?Z?WeM_K>O)puzUX^`J;wL|nZ+A>XXPnOY3uPV5vZlO0f z#9{|_O~>}Mo&xt8T+2IB+dGh5-sAQC^iU(j)c}M*vL6(?OP5SYcDa(Hp_+prxmbn= zCa_tmauyaS3)x8nPS|YK85b%`6ctI$hs8 z@Z}p;zOUQsKTzH3lNyzEAgUWMfXbiO@^-2Q1({zJsk}=95a_T$#~K=HOqBd(Wk@Pj zsR9p$dm^R$FAL{8SVL!=A@Jl})ma*nkYIz6uRFItoAQp!ZnHeRuDP%cN_y)p> z=CuPP^2?(Ka^3x0A&LlIL=fw9 z3|MHvOh?R96(W~Oso$us-SC3))N~jIGSHE@BhPbUOtA=LAdF(((u&z8%GFYM2`GP^ zQU|$gn;my?|J4zR4%T*F(tmB<>b-I8?_Ah@{vG?i(oMP#xQA?tbgyyc8mRu_Flis0 z4hJm3V&cVRc40B^)Ni z1j|_%g|Th9Q5*_HlduzRd-73^IwIcE&d&0tRWjgV21P^`)P>B-1k3`l8Ak;bf<}aE zO4=@zH~=91x#bh}BS#wt4pw)Myk{?{ft!B%fiAn-Ar6wHCZTc#5)EzzC`e cb{I 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 z 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 C@BQ-t diff --git a/docs/source/index.rst b/docs/source/index.rst index ba85fd3..a1b1ef1 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,10 +1,16 @@ -.. image:: images/vector-sdk-img.png +.. image:: images/vector-sdk-alpha.jpg | -========================== -Welcome to the Vector SDK! -========================== +=============================== +Welcome to the Vector SDK Alpha +=============================== + +The Vector SDK gives you direct access to Vector's unprecedented set of advanced sensors, AI capabilities, and robotics technologies including computer vision, intelligent mapping and navigation, and a groundbreaking collection of expressive animations. + +It's powerful but easy to use, complex but not complicated, and versatile enough to be used across a wide range of domains including enterprise, research, and entertainment. + +Please note this is an alpha version of the Vector SDK that is not yet feature complete, but already provides access to many of Vector's hardware and software features. Please visit the official `Anki Developer Forums `_ for more details. .. toctree:: :maxdepth: 1 diff --git a/docs/source/initial.rst b/docs/source/initial.rst index 1b30e8e..3eb9888 100644 --- a/docs/source/initial.rst +++ b/docs/source/initial.rst @@ -18,11 +18,11 @@ To install the SDK on your system, select the instructions for your computer's o .. _trouble: -^^^^^^^^^^^^^^^^^ -Anki SDK Forums -^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^ +Anki Developer Forums +^^^^^^^^^^^^^^^^^^^^^ -Please visit the `Anki SDK Forums `_ to ask questions, find solutions and for general discussion. +Please visit the `Anki Developer Forums `_ to ask questions, find solutions and for general discussion. ---- diff --git a/docs/source/install-linux.rst b/docs/source/install-linux.rst index f31f539..5032c0e 100644 --- a/docs/source/install-linux.rst +++ b/docs/source/install-linux.rst @@ -120,7 +120,7 @@ You will see "SUCCESS!" when this script successfully completes. Troubleshooting ^^^^^^^^^^^^^^^ -Please see the :doc:`Troubleshooting ` page for tips, or visit the `Anki SDK Forums `_ to ask questions, find solutions, or for general discussion. +Please see the :doc:`Troubleshooting ` page for tips, or visit the `Anki Developer Forums `_ to ask questions, find solutions, or for general discussion. ---- diff --git a/docs/source/install-macos.rst b/docs/source/install-macos.rst index e8bd5b4..48768c0 100644 --- a/docs/source/install-macos.rst +++ b/docs/source/install-macos.rst @@ -61,7 +61,7 @@ You will see "SUCCESS!" when this script successfully completes. Troubleshooting ^^^^^^^^^^^^^^^ -Please see the :doc:`Troubleshooting ` page for tips, or visit the `Anki SDK Forums `_ to ask questions, find solutions, or for general discussion. +Please see the :doc:`Troubleshooting ` page for tips, or visit the `Anki Developer Forums `_ to ask questions, find solutions, or for general discussion. ---- diff --git a/docs/source/install-windows.rst b/docs/source/install-windows.rst index 1601e4c..19aaef3 100644 --- a/docs/source/install-windows.rst +++ b/docs/source/install-windows.rst @@ -59,7 +59,7 @@ You will see "SUCCESS!" when this script successfully completes. Troubleshooting ^^^^^^^^^^^^^^^ -Please see the :doc:`Troubleshooting ` page for tips, or visit the `Anki SDK Forums `_ to ask questions, find solutions, or for general discussion. +Please see the :doc:`Troubleshooting ` page for tips, or visit the `Anki Developer Forums `_ to ask questions, find solutions, or for general discussion. ---- diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 4b9d137..7b9da20 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -81,11 +81,11 @@ Can't find Vector's IP address Your Vector IP address looks like "192.168.40.134". Find the IP address from Vector's debug screen: double-click his backpack, move his arms up and down, then look for "IP" on his screen. -^^^^^^^^^^^^^^^ -Anki SDK Forums -^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^ +Anki Developer Forums +^^^^^^^^^^^^^^^^^^^^^ -Please visit the `Anki SDK Forums `_ to ask questions, find solutions and for general discussion. +Please visit the `Anki Developer Forums `_ to ask questions, find solutions and for general discussion. ---- From eb011aaab3b9cfadb28056010ad32da5e2583d01 Mon Sep 17 00:00:00 2001 From: Michelle Sintov Date: Tue, 4 Dec 2018 11:28:33 -0800 Subject: [PATCH 11/13] Fix minor TODOs (#35) --- anki_vector/opengl.py | 3 +-- anki_vector/opengl_viewer.py | 6 +++--- docs/source/conf.py | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/anki_vector/opengl.py b/anki_vector/opengl.py index ac0b5c2..7bc8bb2 100644 --- a/anki_vector/opengl.py +++ b/anki_vector/opengl.py @@ -60,10 +60,9 @@ def raise_opengl_or_pillow_import_error(opengl_import_exc): raise NotImplementedError('GLUT (OpenGL Utility Toolkit) is not available:\n%s' % opengl_import_exc) else: - # TODO Update to: 'Do `pip3 install --user anki_vector[3dviewer]` from `pip3 install PyOpenGL` raise NotImplementedError('OpenGL is not available; ' 'make sure the PyOpenGL and Pillow packages are installed:\n' - 'Do `pip3 install PyOpenGL Pillow` to install. Error: %s' % opengl_import_exc) + 'Do `pip3 install --user "anki_vector[3dviewer]"` to install. Error: %s' % opengl_import_exc) try: diff --git a/anki_vector/opengl_viewer.py b/anki_vector/opengl_viewer.py index de75c9a..31d77b9 100644 --- a/anki_vector/opengl_viewer.py +++ b/anki_vector/opengl_viewer.py @@ -43,7 +43,9 @@ async def my_function(robot): This package requires Python to have the PyOpenGL package installed, along with an implementation of GLUT (OpenGL Utility Toolkit). - To install the Python packages do ``pip install .[3dviewer]`` + To install the Python packages on Mac and Linux do ``python3 -m pip install --user "anki_vector[3dviewer]"`` + + To install the Python packages on Windows do ``py -3 -m pip install --user "anki_vector[3dviewer]"`` On Windows and Linux you must also install freeglut (macOS / OSX has one preinstalled). @@ -56,8 +58,6 @@ async def my_function(robot): script to use it." """ -# TODO Update install line above to: ``pip3 install --user "anki_vector[3dviewer]"`` - # __all__ should order by constants, event classes, other classes, functions. __all__ = ['OpenGLViewer'] diff --git a/docs/source/conf.py b/docs/source/conf.py index e81da14..8f8542f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -382,7 +382,7 @@ def fetch_sdk_version(): texinfo_documents = [ (master_doc, 'VectorSDK', 'Vector SDK Documentation', author, 'VectorSDK', 'The SDK for directly controlling your Vector', - 'Miscellaneous'), # TODO: clean this up + 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. From 4c2e8d995b70257454e9e3cb8636ec8e4b71125c Mon Sep 17 00:00:00 2001 From: Michelle Sintov Date: Wed, 5 Dec 2018 12:58:36 -0800 Subject: [PATCH 12/13] Revise release steps (#37) * rename example files to anki_vector* * change build steps for *.whl, *.tar.gz, and the zip and tar.gz example files to use Makefile (basically same as Cozmo) * Makefile now copies LICENSE.TXT to whl root and examples compressed files root * add minimal readme to examples * etc. --- Makefile | 44 ++++++++++++++++++++++++++++++++++++++ docs/source/downloads.rst | 4 ++-- docs/source/getstarted.rst | 2 +- docs/source/initial.rst | 13 +++++++++++ examples/README.md | 5 +++++ setup.py | 14 +++++++++++- 6 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 Makefile create mode 100644 examples/README.md diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3d0ddfa --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +.PHONY: dist examples license wheel installer + +version = $(shell perl -ne '/__version__ = "([^"]+)/ && print $$1;' anki_vector/version.py) + +license_targets = anki_vector/LICENSE.txt examples/LICENSE.txt +example_targets = dist/anki_vector_sdk_examples.tar.gz dist/anki_vector_sdk_examples.zip + +example_filenames = $(shell cd examples && find . -name '*.py' -o -name '*.txt' -o -name '*.png' -o -name '*.jpg' -o -name '*.md' -o -name '*.json') +example_pathnames = $(shell find examples -name '*.py' -o -name '*.txt' -o -name '*.png' -o -name '*.jpg' -o -name '*.md' -o -name '*.json') +sdist_filename = dist/anki_vector-$(version).tar.gz +wheel_filename = dist/anki_vector-$(version)-py3-none-any.whl + +license: $(license_targets) + +$(license_targets): LICENSE.txt + for fn in $(license_targets); do \ + cp LICENSE.txt $$fn; \ + done + +$(sdist_filename): anki_vector/LICENSE.txt anki_vector/assets/LICENSE.txt $(shell find anki_vector -name '*.py' -o -name '*.mtl' -o -name '*.obj' -o -name '*.jpg') + python3 setup.py sdist + +$(wheel_filename): anki_vector/LICENSE.txt anki_vector/assets/LICENSE.txt $(shell find anki_vector -name '*.py' -o -name '*.mtl' -o -name '*.obj' -o -name '*.jpg') + python3 setup.py bdist_wheel + +dist/anki_vector_sdk_examples.zip: examples/LICENSE.txt $(example_pathnames) + rm -f dist/anki_vector_sdk_examples.zip dist/anki_vector_sdk_examples_$(version).zip + rm -rf dist/anki_vector_sdk_examples_$(version) + mkdir dist/anki_vector_sdk_examples_$(version) + tar -C examples -c $(example_filenames) | tar -C dist/anki_vector_sdk_examples_$(version) -xv + cd dist && zip -r anki_vector_sdk_examples.zip anki_vector_sdk_examples_$(version) + cd dist && zip -r anki_vector_sdk_examples_$(version).zip anki_vector_sdk_examples_$(version) + +dist/anki_vector_sdk_examples.tar.gz: examples/LICENSE.txt $(example_pathnames) + rm -f dist/anki_vector_sdk_examples.tar.gz dist/anki_vector_sdk_examples_$(version).tar.gz + rm -rf dist/anki_vector_sdk_examples_$(version) + mkdir dist/anki_vector_sdk_examples_$(version) + tar -C examples -c $(example_filenames) | tar -C dist/anki_vector_sdk_examples_$(version) -xv + cd dist && tar -cvzf anki_vector_sdk_examples.tar.gz anki_vector_sdk_examples_$(version) + cp -a dist/anki_vector_sdk_examples.tar.gz dist/anki_vector_sdk_examples_$(version).tar.gz + +examples: dist/anki_vector_sdk_examples.tar.gz dist/anki_vector_sdk_examples.zip + +dist: $(sdist_filename) $(wheel_filename) examples diff --git a/docs/source/downloads.rst b/docs/source/downloads.rst index 932d08b..cc0df1d 100644 --- a/docs/source/downloads.rst +++ b/docs/source/downloads.rst @@ -8,9 +8,9 @@ SDK Examples Download Python example scripts that use the Vector SDK. -:verlink:`macOS/Linux SDK Examples ` +:verlink:`macOS/Linux SDK Examples ` -:verlink:`Windows SDK Examples ` +:verlink:`Windows SDK Examples ` ------ GitHub diff --git a/docs/source/getstarted.rst b/docs/source/getstarted.rst index e201307..4f61616 100644 --- a/docs/source/getstarted.rst +++ b/docs/source/getstarted.rst @@ -34,7 +34,7 @@ Prerequisites Starting Up the SDK ------------------- -On the computer, open Terminal (macOS/Linux) or Command Prompt (Windows) and type ``cd vector_sdk_examples``, where *vector_sdk_examples* is the directory you extracted the examples into, and press **Enter**. +On the computer, open Terminal (macOS/Linux) or Command Prompt (Windows) and type ``cd anki_vector_sdk_examples``, where *anki_vector_sdk_examples* is the directory you extracted the examples into, and press **Enter**. ---------------- Example Program diff --git a/docs/source/initial.rst b/docs/source/initial.rst index 3eb9888..3c8de07 100644 --- a/docs/source/initial.rst +++ b/docs/source/initial.rst @@ -14,6 +14,19 @@ To install the SDK on your system, select the instructions for your computer's o * :ref:`install-windows` * :ref:`install-linux` + +-------------------- +SDK Example Programs +-------------------- + +Anki provides example programs for novice and advanced users to run with the SDK. Download the SDK example programs here: + + * :verlink:`macOS/Linux SDK Examples ` + + * :verlink:`Windows SDK Examples ` + +Once downloaded, extract the packaged files to a new directory. + .. .. _trouble: diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..195e77a --- /dev/null +++ b/examples/README.md @@ -0,0 +1,5 @@ +# Example programs for the anki_vector Python SDK + +See the [tutorials](tutorials) directory for a progression of example programs all the way from [hello_world](tutorials/01_hello_world.py) through to more advanced examples. + +See the [apps](apps) directory for some fun fully-featured programs like a remote control. diff --git a/setup.py b/setup.py index f949bcd..6c283d2 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,16 @@ # Copyright (c) 2018 Anki, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License in the file LICENSE.txt or at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. """ Vector SDK, by Anki. @@ -62,7 +74,7 @@ def get_requirements(): ], zip_safe=True, keywords='anki vector robot robotics sdk'.split(), - packages=['anki_vector', 'anki_vector.messaging'], + packages=['anki_vector', 'anki_vector.configure', 'anki_vector.messaging'], package_data={ 'anki_vector': ['LICENSE.txt', 'assets/*.obj', 'assets/*.mtl', 'assets/*.jpg', 'assets/LICENSE.txt'] From 63dfb5f4449b6a1b57c6cc973229c2758d9aaffd Mon Sep 17 00:00:00 2001 From: Michelle Sintov Date: Wed, 5 Dec 2018 14:38:55 -0800 Subject: [PATCH 13/13] Prepare for release: add activate, update verlink, etc. (#40) * add activate script for these env vars: SDK_VERSION TWINE_USER TWINE_PASSWORD S3_BUCKET * refer to those env vars from README build instructions * update path in verlink * update our example links in the doc files to use verlink correctly (though not tested yet) --- docs/source/conf.py | 2 +- docs/source/downloads.rst | 4 ++-- docs/source/initial.rst | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 8f8542f..505b9d5 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -77,7 +77,7 @@ def fetch_sdk_version(): #'sphinx.ext.napoleon' ] -verlink_base_url = 'http://vectorsdk.anki.com/%s/' % sdk_version +verlink_base_url = 'http://developer.anki.com/vector/%s/' % sdk_version verlink_version = sdk_version intersphinx_mapping = { diff --git a/docs/source/downloads.rst b/docs/source/downloads.rst index cc0df1d..863c571 100644 --- a/docs/source/downloads.rst +++ b/docs/source/downloads.rst @@ -8,9 +8,9 @@ SDK Examples Download Python example scripts that use the Vector SDK. -:verlink:`macOS/Linux SDK Examples ` +:verlink:`macOS/Linux SDK Examples ` -:verlink:`Windows SDK Examples ` +:verlink:`Windows SDK Examples ` ------ GitHub diff --git a/docs/source/initial.rst b/docs/source/initial.rst index 3c8de07..719a450 100644 --- a/docs/source/initial.rst +++ b/docs/source/initial.rst @@ -21,9 +21,9 @@ SDK Example Programs Anki provides example programs for novice and advanced users to run with the SDK. Download the SDK example programs here: - * :verlink:`macOS/Linux SDK Examples ` + * :verlink:`macOS/Linux SDK Examples ` - * :verlink:`Windows SDK Examples ` + * :verlink:`Windows SDK Examples ` Once downloaded, extract the packaged files to a new directory.