From 101a8c447eed31f6ed932c57e2aa3bd1f7b95125 Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Tue, 8 Jan 2019 21:16:00 -0500 Subject: [PATCH 01/12] Strip out all components not related to remote control --- .github/CODEOWNERS | 4 - Makefile | 47 - README.md | 21 - anki_vector/__init__.py | 31 - anki_vector/animation.py | 145 - anki_vector/audio.py | 177 - anki_vector/behavior.py | 488 - anki_vector/camera.py | 165 - anki_vector/color.py | 88 - anki_vector/configure/__main__.py | 322 - anki_vector/connection.py | 757 - anki_vector/events.py | 392 - anki_vector/exceptions.py | 145 - anki_vector/faces.py | 388 - anki_vector/lights.py | 210 - anki_vector/messaging/__init__.py | 22 - anki_vector/messaging/alexa.proto | 21 - anki_vector/messaging/alexa_pb2.py | 30 - anki_vector/messaging/alexa_pb2_grpc.py | 3 - anki_vector/messaging/behavior.proto | 64 - anki_vector/messaging/behavior_pb2.py | 321 - anki_vector/messaging/behavior_pb2_grpc.py | 3 - anki_vector/messaging/client.py | 33 - anki_vector/messaging/cube.proto | 366 - anki_vector/messaging/cube_pb2.py | 2239 --- anki_vector/messaging/cube_pb2_grpc.py | 3 - anki_vector/messaging/extensions.proto | 25 - anki_vector/messaging/extensions_pb2.py | 42 - anki_vector/messaging/extensions_pb2_grpc.py | 3 - .../messaging/external_interface.proto | 408 - .../messaging/external_interface_pb2.py | 529 - .../messaging/external_interface_pb2_grpc.py | 944 -- anki_vector/messaging/messages.proto | 920 -- anki_vector/messaging/messages_pb2.py | 6715 -------- anki_vector/messaging/messages_pb2_grpc.py | 3 - anki_vector/messaging/nav_map.proto | 75 - anki_vector/messaging/nav_map_pb2.py | 312 - anki_vector/messaging/nav_map_pb2_grpc.py | 3 - anki_vector/messaging/protocol.py | 33 - anki_vector/messaging/response_status.proto | 32 - anki_vector/messaging/response_status_pb2.py | 114 - .../messaging/response_status_pb2_grpc.py | 3 - anki_vector/messaging/settings.proto | 35 - anki_vector/messaging/settings_pb2.py | 103 - anki_vector/messaging/settings_pb2_grpc.py | 3 - anki_vector/messaging/shared.proto | 98 - anki_vector/messaging/shared_pb2.py | 657 - anki_vector/messaging/shared_pb2_grpc.py | 3 - anki_vector/motors.py | 107 - anki_vector/nav_map.py | 405 - anki_vector/objects.py | 1658 -- anki_vector/opengl/__init__.py | 100 - anki_vector/opengl/assets/LICENSE.txt | 21 - anki_vector/opengl/assets/cube.jpg | Bin 33358 -> 0 bytes anki_vector/opengl/assets/cube.mtl | 9 - anki_vector/opengl/assets/cube.obj | 1000 -- anki_vector/opengl/assets/vector.mtl | 67 - anki_vector/opengl/assets/vector.obj | 13220 ---------------- anki_vector/opengl/opengl.py | 864 - anki_vector/opengl/opengl_vector.py | 603 - anki_vector/opengl/opengl_viewer.py | 679 - anki_vector/photos.py | 145 - anki_vector/proximity.py | 229 - anki_vector/robot.py | 907 -- anki_vector/screen.py | 194 - anki_vector/status.py | 314 - anki_vector/touch.py | 119 - anki_vector/util.py | 1048 -- anki_vector/version.py | 15 - anki_vector/viewer.py | 428 - anki_vector/vision.py | 167 - anki_vector/world.py | 818 - docs/Makefile | 229 - docs/ext/__init__.py | 0 docs/ext/custopoleon/__init__.py | 32 - docs/ext/verlink.py | 72 - docs/source/.gitignore | 1 - docs/source/_static/.gitignore | 3 - docs/source/_templates/layout.html | 94 - docs/source/advanced-tips.rst | 74 - docs/source/api.rst | 46 - docs/source/conf.py | 405 - docs/source/downloads.rst | 25 - docs/source/getstarted.rst | 92 - docs/source/images/custom_markers/LICENSE.txt | 21 - .../images/custom_markers/SDK_2Circles.png | Bin 13336 -> 0 bytes .../images/custom_markers/SDK_2Diamonds.png | Bin 14337 -> 0 bytes .../images/custom_markers/SDK_2Hexagons.png | Bin 11282 -> 0 bytes .../images/custom_markers/SDK_2Triangles.png | Bin 12597 -> 0 bytes .../images/custom_markers/SDK_3Circles.png | Bin 14771 -> 0 bytes .../images/custom_markers/SDK_3Diamonds.png | Bin 16530 -> 0 bytes .../images/custom_markers/SDK_3Hexagons.png | Bin 12156 -> 0 bytes .../images/custom_markers/SDK_3Triangles.png | Bin 13360 -> 0 bytes .../images/custom_markers/SDK_4Circles.png | Bin 13949 -> 0 bytes .../images/custom_markers/SDK_4Diamonds.png | Bin 17717 -> 0 bytes .../images/custom_markers/SDK_4Hexagons.png | Bin 13045 -> 0 bytes .../images/custom_markers/SDK_4Triangles.png | Bin 14179 -> 0 bytes .../images/custom_markers/SDK_5Circles.png | Bin 17357 -> 0 bytes .../images/custom_markers/SDK_5Diamonds.png | Bin 19192 -> 0 bytes .../images/custom_markers/SDK_5Hexagons.png | Bin 15618 -> 0 bytes .../images/custom_markers/SDK_5Triangles.png | Bin 15185 -> 0 bytes docs/source/images/vector-sdk-alpha.jpg | Bin 47066 -> 0 bytes docs/source/index.rst | 56 - docs/source/initial.rst | 44 - docs/source/install-linux.rst | 133 - docs/source/install-macos.rst | 72 - docs/source/install-windows.rst | 70 - docs/source/troubleshooting.rst | 94 - examples/README.md | 5 - examples/apps/3d_viewer/3d_viewer.py | 46 - .../interactive_shell/interactive_shell.py | 57 - .../lib/proximity_mapper_state.py | 354 - .../apps/proximity_mapper/proximity_mapper.py | 314 - examples/apps/remote_control/lib/__init__.py | 0 .../apps/remote_control/lib/flask_helpers.py | 110 - examples/face_images/cozmo_image.jpg | Bin 6618 -> 0 bytes examples/tutorials/01_hello_world.py | 33 - examples/tutorials/02_drive_square.py | 44 - examples/tutorials/03_motors.py | 73 - examples/tutorials/04_animation.py | 41 - examples/tutorials/05_play_behaviors.py | 35 - examples/tutorials/06_face_image.py | 55 - examples/tutorials/07_dock_with_cube.py | 62 - .../08_drive_to_cliff_and_back_up.py | 66 - examples/tutorials/09_show_photo.py | 50 - examples/tutorials/10_eye_color.py | 36 - .../tutorials/11_face_event_subscription.py | 69 - .../tutorials/12_wake_word_subscription.py | 62 - examples/tutorials/13_custom_objects.py | 122 - .../remote_control.py | 2 +- requirements.txt | 9 +- setup.py | 86 - 132 files changed, 3 insertions(+), 43953 deletions(-) delete mode 100644 .github/CODEOWNERS delete mode 100644 Makefile delete mode 100644 README.md delete mode 100755 anki_vector/__init__.py delete mode 100644 anki_vector/animation.py delete mode 100644 anki_vector/audio.py delete mode 100644 anki_vector/behavior.py delete mode 100644 anki_vector/camera.py delete mode 100755 anki_vector/color.py delete mode 100755 anki_vector/configure/__main__.py delete mode 100644 anki_vector/connection.py delete mode 100644 anki_vector/events.py delete mode 100644 anki_vector/exceptions.py delete mode 100644 anki_vector/faces.py delete mode 100755 anki_vector/lights.py delete mode 100644 anki_vector/messaging/__init__.py delete mode 100644 anki_vector/messaging/alexa.proto delete mode 100644 anki_vector/messaging/alexa_pb2.py delete mode 100644 anki_vector/messaging/alexa_pb2_grpc.py delete mode 100644 anki_vector/messaging/behavior.proto delete mode 100644 anki_vector/messaging/behavior_pb2.py delete mode 100644 anki_vector/messaging/behavior_pb2_grpc.py delete mode 100644 anki_vector/messaging/client.py delete mode 100644 anki_vector/messaging/cube.proto delete mode 100644 anki_vector/messaging/cube_pb2.py delete mode 100644 anki_vector/messaging/cube_pb2_grpc.py delete mode 100644 anki_vector/messaging/extensions.proto delete mode 100644 anki_vector/messaging/extensions_pb2.py delete mode 100644 anki_vector/messaging/extensions_pb2_grpc.py delete mode 100644 anki_vector/messaging/external_interface.proto delete mode 100644 anki_vector/messaging/external_interface_pb2.py delete mode 100644 anki_vector/messaging/external_interface_pb2_grpc.py delete mode 100644 anki_vector/messaging/messages.proto delete mode 100644 anki_vector/messaging/messages_pb2.py delete mode 100644 anki_vector/messaging/messages_pb2_grpc.py delete mode 100644 anki_vector/messaging/nav_map.proto delete mode 100644 anki_vector/messaging/nav_map_pb2.py delete mode 100644 anki_vector/messaging/nav_map_pb2_grpc.py delete mode 100644 anki_vector/messaging/protocol.py delete mode 100644 anki_vector/messaging/response_status.proto delete mode 100644 anki_vector/messaging/response_status_pb2.py delete mode 100644 anki_vector/messaging/response_status_pb2_grpc.py delete mode 100644 anki_vector/messaging/settings.proto delete mode 100644 anki_vector/messaging/settings_pb2.py delete mode 100644 anki_vector/messaging/settings_pb2_grpc.py delete mode 100644 anki_vector/messaging/shared.proto delete mode 100644 anki_vector/messaging/shared_pb2.py delete mode 100644 anki_vector/messaging/shared_pb2_grpc.py delete mode 100644 anki_vector/motors.py delete mode 100755 anki_vector/nav_map.py delete mode 100644 anki_vector/objects.py delete mode 100644 anki_vector/opengl/__init__.py delete mode 100644 anki_vector/opengl/assets/LICENSE.txt delete mode 100644 anki_vector/opengl/assets/cube.jpg delete mode 100644 anki_vector/opengl/assets/cube.mtl delete mode 100644 anki_vector/opengl/assets/cube.obj delete mode 100644 anki_vector/opengl/assets/vector.mtl delete mode 100644 anki_vector/opengl/assets/vector.obj delete mode 100644 anki_vector/opengl/opengl.py delete mode 100644 anki_vector/opengl/opengl_vector.py delete mode 100644 anki_vector/opengl/opengl_viewer.py delete mode 100644 anki_vector/photos.py delete mode 100755 anki_vector/proximity.py delete mode 100755 anki_vector/robot.py delete mode 100644 anki_vector/screen.py delete mode 100644 anki_vector/status.py delete mode 100755 anki_vector/touch.py delete mode 100644 anki_vector/util.py delete mode 100644 anki_vector/version.py delete mode 100644 anki_vector/viewer.py delete mode 100644 anki_vector/vision.py delete mode 100644 anki_vector/world.py delete mode 100644 docs/Makefile delete mode 100644 docs/ext/__init__.py delete mode 100644 docs/ext/custopoleon/__init__.py delete mode 100644 docs/ext/verlink.py delete mode 100644 docs/source/.gitignore delete mode 100644 docs/source/_static/.gitignore delete mode 100644 docs/source/_templates/layout.html delete mode 100644 docs/source/advanced-tips.rst delete mode 100644 docs/source/api.rst delete mode 100644 docs/source/conf.py delete mode 100644 docs/source/downloads.rst delete mode 100644 docs/source/getstarted.rst delete mode 100644 docs/source/images/custom_markers/LICENSE.txt delete mode 100644 docs/source/images/custom_markers/SDK_2Circles.png delete mode 100644 docs/source/images/custom_markers/SDK_2Diamonds.png delete mode 100644 docs/source/images/custom_markers/SDK_2Hexagons.png delete mode 100644 docs/source/images/custom_markers/SDK_2Triangles.png delete mode 100644 docs/source/images/custom_markers/SDK_3Circles.png delete mode 100644 docs/source/images/custom_markers/SDK_3Diamonds.png delete mode 100644 docs/source/images/custom_markers/SDK_3Hexagons.png delete mode 100644 docs/source/images/custom_markers/SDK_3Triangles.png delete mode 100644 docs/source/images/custom_markers/SDK_4Circles.png delete mode 100644 docs/source/images/custom_markers/SDK_4Diamonds.png delete mode 100644 docs/source/images/custom_markers/SDK_4Hexagons.png delete mode 100644 docs/source/images/custom_markers/SDK_4Triangles.png delete mode 100644 docs/source/images/custom_markers/SDK_5Circles.png delete mode 100644 docs/source/images/custom_markers/SDK_5Diamonds.png delete mode 100644 docs/source/images/custom_markers/SDK_5Hexagons.png delete mode 100644 docs/source/images/custom_markers/SDK_5Triangles.png delete mode 100644 docs/source/images/vector-sdk-alpha.jpg delete mode 100644 docs/source/index.rst delete mode 100644 docs/source/initial.rst delete mode 100644 docs/source/install-linux.rst delete mode 100644 docs/source/install-macos.rst delete mode 100644 docs/source/install-windows.rst delete mode 100644 docs/source/troubleshooting.rst delete mode 100644 examples/README.md delete mode 100755 examples/apps/3d_viewer/3d_viewer.py delete mode 100755 examples/apps/interactive_shell/interactive_shell.py delete mode 100755 examples/apps/proximity_mapper/lib/proximity_mapper_state.py delete mode 100755 examples/apps/proximity_mapper/proximity_mapper.py delete mode 100644 examples/apps/remote_control/lib/__init__.py delete mode 100644 examples/apps/remote_control/lib/flask_helpers.py delete mode 100644 examples/face_images/cozmo_image.jpg delete mode 100755 examples/tutorials/01_hello_world.py delete mode 100755 examples/tutorials/02_drive_square.py delete mode 100755 examples/tutorials/03_motors.py delete mode 100755 examples/tutorials/04_animation.py delete mode 100755 examples/tutorials/05_play_behaviors.py delete mode 100755 examples/tutorials/06_face_image.py delete mode 100755 examples/tutorials/07_dock_with_cube.py delete mode 100755 examples/tutorials/08_drive_to_cliff_and_back_up.py delete mode 100755 examples/tutorials/09_show_photo.py delete mode 100755 examples/tutorials/10_eye_color.py delete mode 100755 examples/tutorials/11_face_event_subscription.py delete mode 100755 examples/tutorials/12_wake_word_subscription.py delete mode 100755 examples/tutorials/13_custom_objects.py rename {examples/apps/remote_control => remote_control}/remote_control.py (99%) delete mode 100644 setup.py diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index efe1780..0000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# https://help.github.com/articles/about-codeowners/ - -# These owners will be the default owners for everything in the repo. -* @anki/cozmo-sdk-admins diff --git a/Makefile b/Makefile deleted file mode 100644 index ecd3565..0000000 --- a/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -.PHONY: clean 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/opengl/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/opengl/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 - -clean: - rm -rf dist diff --git a/README.md b/README.md deleted file mode 100644 index 4c37c1a..0000000 --- a/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Anki Vector - Python SDK - -![Vector](docs/source/images/vector-sdk-alpha.jpg) - -Learn more about Vector: https://www.anki.com/vector - -Learn more about the SDK: https://developer.anki.com/ - -SDK documentation: http://developer.anki.com/vector/docs/ - -Forums: https://forums.anki.com/ - - -## Getting Started - -You can follow steps [here](http://developer.anki.com/vector/docs/) to set up your Vector robot with the SDK. - - -## Privacy Policy and Terms and Conditions - -Use of Vector and the Vector SDK is subject to Anki's [Privacy Policy](https://www.anki.com/en-us/company/privacy) and [Terms and Conditions](https://www.anki.com/en-us/company/terms-and-conditions). diff --git a/anki_vector/__init__.py b/anki_vector/__init__.py deleted file mode 100755 index e4cbc9a..0000000 --- a/anki_vector/__init__.py +++ /dev/null @@ -1,31 +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 -# -# https://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. - -""" -SDK for programming with the Anki Vector robot. -""" - -import sys -import logging - -from . import messaging -from .robot import Robot, AsyncRobot -from .version import __version__ - -logger = logging.getLogger('vector') # pylint: disable=invalid-name - -if sys.version_info < (3, 6, 1): - sys.exit('anki_vector requires Python 3.6.1 or later') - -__all__ = ['Robot', 'AsyncRobot', 'logger', 'messaging', '__version__'] diff --git a/anki_vector/animation.py b/anki_vector/animation.py deleted file mode 100644 index d7ad9b4..0000000 --- a/anki_vector/animation.py +++ /dev/null @@ -1,145 +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 -# -# https://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. - -""" -Animation related classes, functions, events and values. - -Animations represent a sequence of highly coordinated movements, faces, lights, and sounds used to demonstrate an emotion or reaction. - -Animations can control the following tracks: head, lift, treads, face, audio and backpack lights. - -There are two ways to play an animation on Vector: play_animation and play_animation_trigger (not yet implemented). When calling play_animation, -you select the specific animation you want the robot to run. For play_animation_trigger, you select a group of animations, and the robot -will choose which animation from the group to run when you execute the method. - -By default, when an SDK program starts, the SDK will request a list of known animations from the robot, which will be loaded into anim_list -in the AnimationComponent. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ["AnimationComponent"] - -import concurrent - -from . import connection, exceptions, util -from .messaging import protocol - - -class AnimationComponent(util.Component): - """Play animations on the robot""" - - def __init__(self, robot): - super().__init__(robot) - self._anim_dict = {} - - @property - def anim_list(self): - """ - Holds the set of animation names (strings) returned from the robot. - - Animation names are dynamically retrieved from the robot when the Python - script connects to it. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - print("List all animation names:") - anim_names = robot.anim.anim_list - for anim_name in anim_names: - print(anim_name) - """ - if not self._anim_dict: - self.logger.warning("Anim list was empty. Lazy-loading anim list now.") - result = self.load_animation_list() - if isinstance(result, concurrent.futures.Future): - result.result() - return list(self._anim_dict.keys()) - - async def _ensure_loaded(self): - """ - This is an optimization for the case where a user doesn't - need the animation_list. This way, connections aren't delayed - by the load_animation_list call. - - If this is invoked inside another async function then we - explicitly await the result. - """ - if not self._anim_dict: - self.logger.warning("Anim list was empty. Lazy-loading anim list now.") - await self._load_animation_list() - - async def _load_animation_list(self): - req = protocol.ListAnimationsRequest() - result = await self.grpc_interface.ListAnimations(req) - self.logger.debug(f"status: {result.status}, number_of_animations:{len(result.animation_names)}") - self._anim_dict = {a.name: a for a in result.animation_names} - return result - - @connection.on_connection_thread(log_messaging=False, requires_control=False) - async def load_animation_list(self): - """Request the list of animations from the robot - - When the request has completed, anim_list will be populated with - the list of animations the robot knows how to run. - - .. testcode:: - - import anki_vector - - with anki_vector.AsyncRobot() as robot: - anim_request = robot.anim.load_animation_list() - anim_request.result() - anim_names = robot.anim.anim_list - for anim_name in anim_names: - print(anim_name) - """ - return await self._load_animation_list() - - @connection.on_connection_thread() - async def play_animation(self, anim: str, loop_count: int = 1, ignore_body_track: bool = False, ignore_head_track: bool = False, ignore_lift_track: bool = False): - """Starts an animation playing on a robot. - - Vector must be off of the charger to play an animation. - - Warning: Specific animations may be renamed or removed in future updates of the app. - If you want your program to work more reliably across all versions - we recommend using :meth:`play_animation_trigger` instead. (:meth:`play_animation_trigger` is still in development.) - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.anim.play_animation('anim_turn_left_01') - - :param anim: The animation to play. Can be of type str or :class:`anki_vector.protocol.Animation`. - :param loop_count: Number of times to play the animation. - :param ignore_body_track: True to ignore the animation track for Vector's body (i.e. the wheels / treads). - :param ignore_head_track: True to ignore the animation track for Vector's head. - :param ignore_lift_track: True to ignore the animation track for Vector's lift. - """ - animation = anim - if not isinstance(anim, protocol.Animation): - await self._ensure_loaded() - if anim not in self.anim_list: - raise exceptions.VectorException(f"Unknown animation: {anim}") - animation = self._anim_dict[anim] - req = protocol.PlayAnimationRequest(animation=animation, - loops=loop_count, - ignore_body_track=ignore_body_track, - ignore_head_track=ignore_head_track, - ignore_lift_track=ignore_lift_track) - return await self.grpc_interface.PlayAnimation(req) diff --git a/anki_vector/audio.py b/anki_vector/audio.py deleted file mode 100644 index 553e62c..0000000 --- a/anki_vector/audio.py +++ /dev/null @@ -1,177 +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 -# -# https://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. - -"""Support for Vector's audio. - -Vector has multiple built-in microphones which can detect sound and the direction -it originated from. The robot then processing this data into a single-channel -audio feed available to the SDK. - -The :class:`AudioComponent` class defined in this module is made available as -:attr:`anki_vector.robot.Robot.audio` and can be used to enable/disable audio -sending and process audio data being sent by the robot. -""" - -# TODO Not yet supported. Audio from robot does not yet reach Python. - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['AudioComponent'] - -import asyncio -from concurrent.futures import CancelledError -import sys - -from . import util -from .events import Events -from .messaging import protocol - -try: - import numpy as np -except ImportError: - sys.exit("Cannot import numpy: Do `pip3 install numpy` to install") - - -class AudioComponent(util.Component): - """Represents Vector's audio feed. - - The AudioComponent object receives audio data from Vector's microphones. - - The :class:`anki_vector.robot.Robot` or :class:`anki_vector.robot.AsyncRobot` instance owns this audio component. - - .. code-block:: python - - import time - - try: - from scipy.io import wavfile - except ImportError as exc: - sys.exit("Cannot import scipy: Do `pip3 install scipy` to install") - - with anki_vector.Robot(enable_audio_feed=True) as robot: - time.sleep(5.0) - wavfile.write("outputfile.wav", anki_vector.protocol.PROCESSED_SAMPLE_RATE, robot.audio.raw_audio_waveform_history) - - :param robot: A reference to the owner Robot object. - """ - # TODO When audio is ready, convert `.. code-block:: python` to `.. testcode::` - - def __init__(self, robot): - super().__init__(robot) - - # @TODO: Add a variable tracking the current audio send mode once that is implemented - self._raw_audio_waveform_history = np.empty(0, dtype=np.int16) - self._latest_sample_id = None - - self._audio_processing_mode = None - - self._audio_feed_task: asyncio.Task = None - - # Subscribe to callbacks related to objects - robot.events.subscribe(self._on_audio_send_mode_changed_event, - Events.audio_send_mode_changed) - - # @TODO: Implement audio history as ringbuffer, caching only recent audio - # @TODO: Add function to return a recent chunk of the audio history in a standardized python audio package format - # @TODO: Add hook for feeding audio stream into external voice processing library - - @property - def raw_audio_waveform_history(self) -> np.array: - """:class:`np.array`: The most recent processed image received from the robot. - - Audio history as signed-16-bit-int waveform data - - The sample rate from the microphones is 15625, which the robot processes at 16khz. As a result the pitch is - altered by 2.4%. The last 16000 values of the array represent the last second of audio amplitude. - - .. code-block:: python - - import time - - try: - from scipy.io import wavfile - except ImportError as exc: - sys.exit("Cannot import scipy: Do `pip3 install scipy` to install") - - with anki_vector.Robot(enable_audio_feed=True) as robot: - time.sleep(5.0) - wavfile.write("outputfile.wav", anki_vector.protocol.PROCESSED_SAMPLE_RATE, robot.audio.raw_audio_waveform_history) - - :getter: Returns the numpy array representing the audio history - """ - # TODO When audio is ready, convert `.. code-block:: python` to `.. testcode::` - return self._raw_audio_waveform_history - - @property - def latest_sample_id(self) -> int: - """The most recent received audio sample from the robot. - - :getter: Returns the id for the latest audio sample - """ - return self._latest_sample_id - - @property - def audio_processing_mode(self) -> protocol.AudioProcessingMode: - """The current mode which the robot is processing audio. - - :getter: Returns the most recently received audio processing mode on the robot - """ - return self._audio_processing_mode - - def init_audio_feed(self) -> None: - """Begin audio feed task""" - if not self._audio_feed_task or self._audio_feed_task.done(): - self._audio_feed_task = self.robot.loop.create_task(self._request_and_handle_audio()) - - def close_audio_feed(self) -> None: - """Cancel audio feed task""" - if self._audio_feed_task: - self._audio_feed_task.cancel() - self.robot.loop.run_until_complete(self._audio_feed_task) - - def _on_audio_send_mode_changed_event(self, msg: protocol.AudioSendModeChanged) -> None: - """Callback for changes in the robot's audio processing mode""" - self._audio_processing_mode = msg.mode - self.logger.info('Audio processing mode changed to {0}'.format(self._audio_processing_mode)) - - def _process_audio(self, msg: protocol.AudioFeedResponse) -> None: - """Processes raw data from the robot into a more more useful image structure.""" - audio_data = msg.signal_power - - expected_size = protocol.SAMPLE_COUNTS_PER_SDK_MESSAGE - received_size = int(len(audio_data) / 2) - if expected_size != received_size: - self.logger.warning('WARNING: audio stream received {0} bytes while {1} were expected'.format(received_size, expected_size)) - - # Constuct numpy array out of source data - array = np.frombuffer(audio_data, dtype=np.int16, count=received_size, offset=0) - - # Append to audio history - self._raw_audio_waveform_history = np.append(self._raw_audio_waveform_history, array) - - self._latest_sample_id = len(self._raw_audio_waveform_history) - 1 - - async def _request_and_handle_audio(self) -> None: - """Queries and listens for audio feed events from the robot. - Received events are parsed by a helper function.""" - try: - req = protocol.AudioFeedRequest() - async for evt in self.grpc_interface.AudioFeed(req): - # If the audio feed is disabled after stream is setup, exit the stream - # (the audio feed on the robot is disabled internally on stream exit) - if not self.robot.enable_audio_feed: - self.logger.warning('Audio feed has been disabled. Enable the feed to start/continue receiving audio feed data') - return - self._process_audio(evt) - except CancelledError: - self.logger.debug('Audio feed task was cancelled. This is expected during disconnection.') diff --git a/anki_vector/behavior.py b/anki_vector/behavior.py deleted file mode 100644 index eb652fc..0000000 --- a/anki_vector/behavior.py +++ /dev/null @@ -1,488 +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 -# -# https://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. - -""" -.. _behavior: - -Behavior related classes and functions. - -Behaviors represent a complex task which requires Vector's -internal logic to determine how long it will take. This -may include combinations of animation, path planning or -other functionality. Examples include :meth:`drive_on_charger`, -:meth:`set_lift_height`, etc. - -For commands such as :meth:`go_to_pose`, :meth:`drive_on_charger` and :meth:`dock_with_cube`, -Vector uses path planning, which refers to the problem of -navigating the robot from point A to B without collisions. Vector -loads known obstacles from his map, creates a path to navigate -around those objects, then starts following the path. If a new obstacle -is found while following the path, a new plan may be created. - -The :class:`BehaviorComponent` class in this module contains -functions for all the behaviors. -""" - -__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 -from .messaging import protocol - -# Constants - -#: 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 -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 - -#: The lowest height-above-ground that lift can be moved to -MIN_LIFT_HEIGHT = util.distance_mm(MIN_LIFT_HEIGHT_MM) - -# The largest height-above-ground that lift can be moved to in millimeters. -MAX_LIFT_HEIGHT_MM = 92.0 - -#: The largest height-above-ground that lift can be moved to -MAX_LIFT_HEIGHT = util.distance_mm(MAX_LIFT_HEIGHT_MM) - - -# TODO: Expose is_active and priority states of the SDK behavior control from this class. -class BehaviorComponent(util.Component): - """Run behaviors on Vector""" - - _next_action_id = protocol.FIRST_SDK_TAG - - def __init__(self, robot): - super().__init__(robot) - self._motion_profile_map = {} - - # TODO Make the motion_profile_map into a class. - @property - def motion_profile_map(self) -> dict: - """Tells Vector how to drive when receiving navigation and movement actions - such as go_to_pose and dock_with_cube. - - motion_prof_map values are as follows: - | speed_mmps (float) - | accel_mmps2 (float) - | decel_mmps2 (float) - | point_turn_speed_rad_per_sec (float) - | point_turn_accel_rad_per_sec2 (float) - | point_turn_decel_rad_per_sec2 (float) - | dock_speed_mmps (float) - | dock_accel_mmps2 (float) - | dock_decel_mmps2 (float) - | reverse_speed_mmps (float) - | is_custom (bool) - - :getter: Returns the motion profile map - :setter: Sets the motion profile map - - :param motion_prof_map: Provide custom speed, acceleration and deceleration - values with which the robot goes to the given pose. - """ - return self._motion_profile_map - - @motion_profile_map.setter - def motion_profile_map(self, motion_profile_map: dict): - self._motion_profile_map = motion_profile_map - - def _motion_profile_for_proto(self) -> protocol.PathMotionProfile: - """Packages the current motion profile into a proto object - - Returns: - A profile object describing motion which can be passed with any proto action message. - """ - # TODO: This should be made into its own class - default_motion_profile = { - "speed_mmps": 100.0, - "accel_mmps2": 200.0, - "decel_mmps2": 500.0, - "point_turn_speed_rad_per_sec": 2.0, - "point_turn_accel_rad_per_sec2": 10.0, - "point_turn_decel_rad_per_sec2": 10.0, - "dock_speed_mmps": 60.0, - "dock_accel_mmps2": 200.0, - "dock_decel_mmps2": 500.0, - "reverse_speed_mmps": 80.0, - "is_custom": 1 if self._motion_profile_map else 0 - } - default_motion_profile.update(self._motion_profile_map) - - return protocol.PathMotionProfile(**default_motion_profile) - - @classmethod - def _get_next_action_id(cls): - # Post increment _current_action_id (and loop within the SDK_TAG range) - next_action_id = cls._next_action_id - if cls._next_action_id == protocol.LAST_SDK_TAG: - cls._next_action_id = protocol.FIRST_SDK_TAG - else: - cls._next_action_id += 1 - return next_action_id - - # Navigation actions - @connection.on_connection_thread() - async def drive_off_charger(self): - """Drive Vector off the charger - - If Vector is on the charger, drives him off the charger. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.behavior.drive_off_charger() - """ - drive_off_charger_request = protocol.DriveOffChargerRequest() - return await self.grpc_interface.DriveOffCharger(drive_off_charger_request) - - @connection.on_connection_thread() - async def drive_on_charger(self): - """Drive Vector onto the charger - - Vector will attempt to find the charger and, if successful, he will - back onto it and start charging. - - Vector's charger has a visual marker so that the robot can locate it - for self-docking. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.behavior.drive_on_charger() - """ - drive_on_charger_request = protocol.DriveOnChargerRequest() - return await self.grpc_interface.DriveOnCharger(drive_on_charger_request) - - @connection.on_connection_thread() - async def set_eye_color(self, hue: float, saturation: float) -> protocol.SetEyeColorResponse: - """Set Vector's eye color. - - .. testcode:: - - import anki_vector - import time - - with anki_vector.Robot() as robot: - print("Set Vector's eye color to purple...") - robot.behavior.set_eye_color(0.83, 0.76) - time.sleep(5) - - :param hue: The hue to use for Vector's eyes. - :param saturation: The saturation to use for Vector's eyes. - """ - eye_color_request = protocol.SetEyeColorRequest(hue=hue, saturation=saturation) - return await self.conn.grpc_interface.SetEyeColor(eye_color_request) - - @connection.on_connection_thread() - async def go_to_pose(self, - pose: util.Pose, - relative_to_robot: bool = False, - num_retries: int = 0) -> protocol.GoToPoseResponse: - """Tells Vector to drive to the specified pose and orientation. - - In navigating to the requested pose, Vector will use path planning. - - If relative_to_robot is set to True, the given pose will assume the - robot's pose as its origin. - - Since the robot understands position by monitoring its tread movement, - it does not understand movement in the z axis. This means that the only - applicable elements of pose in this situation are position.x position.y - and rotation.angle_z. - - :param pose: The destination pose. - :param relative_to_robot: Whether the given pose is relative to - the robot's pose. - :param num_retries: Number of times to re-attempt action in case of a failure. - - Returns: - A response from the robot with status information sent when this request successfully completes or fails. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, Pose - - with anki_vector.Robot() as robot: - pose = Pose(x=50, y=0, z=0, angle_z=anki_vector.util.Angle(degrees=0)) - robot.behavior.go_to_pose(pose) - """ - if relative_to_robot and self.robot.pose: - pose = self.robot.pose.define_pose_relative_this(pose) - - motion_prof = self._motion_profile_for_proto() - # @TODO: the id_tag we supply can be used to cancel this action, - # however when we implement that we will need some way to get the id_tag - # out of this function. - go_to_pose_request = protocol.GoToPoseRequest(x_mm=pose.position.x, - y_mm=pose.position.y, - rad=pose.rotation.angle_z.radians, - motion_prof=motion_prof, - id_tag=self._get_next_action_id(), - num_retries=num_retries) - - return await self.grpc_interface.GoToPose(go_to_pose_request) - - # TODO alignment_type coming out ugly in the docs without real values - @connection.on_connection_thread() - async def dock_with_cube(self, - target_object: objects.LightCube, - approach_angle: util.Angle = None, - alignment_type: protocol.AlignmentType = protocol.ALIGNMENT_TYPE_LIFT_PLATE, - distance_from_marker: util.Distance = None, - num_retries: int = 0) -> protocol.DockWithCubeResponse: - """Tells Vector to dock with a light cube, optionally using a given approach angle and distance. - - While docking with the cube, Vector will use path planning. - - :param target_object: The LightCube object to dock with. - :param approach_angle: Angle to approach the dock with. - :param alignment_type: Which part of the robot to align with the object. - :param distance_from_marker: How far from the object to approach (0 to dock) - :param num_retries: Number of times to re-attempt action in case of a failure. - - Returns: - A response from the robot with status information sent when this request successfully completes or fails. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - - if robot.world.connected_light_cube: - dock_response = robot.behavior.dock_with_cube(robot.world.connected_light_cube) - docking_result = dock_response.result - """ - if target_object is None: - raise Exception("Must supply a target_object to dock_with_cube") - - motion_prof = self._motion_profile_for_proto() - - # @TODO: the id_tag we supply can be used to cancel this action, - # however when we implement that we will need some way to get the id_tag - # out of this function. - dock_request = protocol.DockWithCubeRequest(object_id=target_object.object_id, - alignment_type=alignment_type, - motion_prof=motion_prof, - id_tag=self._get_next_action_id(), - num_retries=num_retries) - if approach_angle is not None: - dock_request.use_approach_angle = True - dock_request.use_pre_dock_pose = True - dock_request.approach_angle = approach_angle.radians - if distance_from_marker is not None: - dock_request.distance_from_marker = distance_from_marker.distance_mm - - return await self.grpc_interface.DockWithCube(dock_request) - - # Movement actions - @connection.on_connection_thread() - async def drive_straight(self, - distance: util.Distance, - speed: util.Speed, - should_play_anim: bool = True, - num_retries: int = 0) -> protocol.DriveStraightResponse: - """Tells Vector to drive in a straight line. - - Vector will drive for the specified distance (forwards or backwards) - - Vector must be off of the charger for this movement action. - - :param distance: The distance to drive - (>0 for forwards, <0 for backwards) - :param speed: The speed to drive at - (should always be >0, the abs(speed) is used internally) - :param should_play_anim: Whether to play idle animations whilst driving - (tilt head, hum, animated eyes, etc.) - :param num_retries: Number of times to re-attempt action in case of a failure. - - Returns: - A response from the robot with status information sent when this request successfully completes or fails. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, distance_mm, speed_mmps - - with anki_vector.Robot() as robot: - robot.behavior.drive_straight(distance_mm(200), speed_mmps(100)) - """ - - # @TODO: the id_tag we supply can be used to cancel this action, - # however when we implement that we will need some way to get the id_tag - # out of this function. - drive_straight_request = protocol.DriveStraightRequest(speed_mmps=speed.speed_mmps, - dist_mm=distance.distance_mm, - should_play_animation=should_play_anim, - id_tag=self._get_next_action_id(), - num_retries=num_retries) - - return await self.grpc_interface.DriveStraight(drive_straight_request) - - @connection.on_connection_thread() - async def turn_in_place(self, - angle: util.Angle, - speed: util.Angle = util.Angle(0.0), - accel: util.Angle = util.Angle(0.0), - angle_tolerance: util.Angle = util.Angle(0.0), - is_absolute: bool = 0, - num_retries: int = 0) -> protocol.TurnInPlaceResponse: - """Turn the robot around its current position. - - Vector must be off of the charger for this movement action. - - :param angle: The angle to turn. Positive - values turn to the left, negative values to the right. - :param speed: Angular turn speed (per second). - :param accel: Acceleration of angular turn - (per second squared). - :param angle_tolerance: angular tolerance - to consider the action complete (this is clamped to a minimum - of 2 degrees internally). - :param is_absolute: True to turn to a specific angle, False to - turn relative to the current pose. - :param num_retries: Number of times to re-attempt the turn in case of a failure. - - Returns: - A response from the robot with status information sent when this request successfully completes or fails. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees - - with anki_vector.Robot() as robot: - robot.behavior.turn_in_place(degrees(90)) - """ - turn_in_place_request = protocol.TurnInPlaceRequest(angle_rad=angle.radians, - speed_rad_per_sec=speed.radians, - accel_rad_per_sec2=accel.radians, - tol_rad=angle_tolerance.radians, - is_absolute=is_absolute, - id_tag=self._get_next_action_id(), - num_retries=num_retries) - - return await self.grpc_interface.TurnInPlace(turn_in_place_request) - - @connection.on_connection_thread() - async def set_head_angle(self, - angle: util.Angle, - accel: float = 10.0, - max_speed: float = 10.0, - duration: float = 0.0, - num_retries: int = 0) -> protocol.SetHeadAngleResponse: - """Tell Vector's head to move to a given angle. - - :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 - of zero will make Vector try to do it as quickly as possible. - :param num_retries: Number of times to re-attempt the action in case of a failure. - - Returns: - A response from the robot with status information sent when this request successfully completes or fails. - - .. testcode:: - - 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: - # 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, - duration_sec=duration, - id_tag=self._get_next_action_id(), - num_retries=num_retries) - - return await self.grpc_interface.SetHeadAngle(set_head_angle_request) - - @connection.on_connection_thread() - async def set_lift_height(self, - height: float, - accel: float = 10.0, - max_speed: float = 10.0, - duration: float = 0.0, - num_retries: int = 0) -> protocol.SetLiftHeightResponse: - """Tell Vector's lift to move to a given height. - - :param height: desired height for Vector's lift 0.0 (bottom) to - 1.0 (top) (we clamp it to this range internally). - :param accel: Acceleration of Vector's lift in radians per - second squared. - :param max_speed: Maximum speed of Vector's lift in radians per second. - :param duration: Time for Vector's lift to move in seconds. A value - of zero will make Vector try to do it as quickly as possible. - :param num_retries: Number of times to re-attempt the action in case of a failure. - - Returns: - A response from the robot with status information sent when this request successfully completes or fails. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.behavior.set_lift_height(1.0) - """ - - if height < 0.0: - self.logger.warning("lift height %s too small, should be in 0..1 range - clamping", height) - height = MIN_LIFT_HEIGHT_MM - elif height > 1.0: - self.logger.warning("lift height %s too large, should be in 0..1 range - clamping", height) - height = MAX_LIFT_HEIGHT_MM - else: - height = MIN_LIFT_HEIGHT_MM + (height * (MAX_LIFT_HEIGHT_MM - MIN_LIFT_HEIGHT_MM)) - - set_lift_height_request = protocol.SetLiftHeightRequest(height_mm=height, - max_speed_rad_per_sec=max_speed, - accel_rad_per_sec2=accel, - duration_sec=duration, - id_tag=self._get_next_action_id(), - num_retries=num_retries) - - return await self.grpc_interface.SetLiftHeight(set_lift_height_request) diff --git a/anki_vector/camera.py b/anki_vector/camera.py deleted file mode 100644 index 64bf499..0000000 --- a/anki_vector/camera.py +++ /dev/null @@ -1,165 +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 -# -# https://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. - -"""Support for Vector's camera. - -Vector has a built-in camera which he uses to observe the world around him. - -The :class:`CameraComponent` class defined in this module is made available as -:attr:`anki_vector.robot.Robot.camera` and can be used to enable/disable image -sending and observe images being sent by the robot. - -The camera resolution is 1280 x 720 with a field of view of 90 deg (H) x 50 deg (V). -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['CameraComponent'] - -import asyncio -from concurrent.futures import CancelledError -import sys - -try: - import cv2 -except ImportError: - sys.exit("Cannot import opencv-python: Do `pip3 install opencv-python` to install") - -from . import util -from .messaging import protocol - -try: - import numpy as np -except ImportError: - sys.exit("Cannot import numpy: Do `pip3 install numpy` to install") - -try: - from PIL import Image -except ImportError: - sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install") - - -class CameraComponent(util.Component): - """Represents Vector's camera. - - The CameraComponent object receives images from Vector's camera, unpacks the data, - composes it and makes it available as latest_image. - - The :class:`anki_vector.robot.Robot` or :class:`anki_vector.robot.AsyncRobot` instance observes the camera. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_camera_feed=True) as robot: - image = robot.camera.latest_image - image.show() - - :param robot: A reference to the owner Robot object. - """ - - def __init__(self, robot): - super().__init__(robot) - - self._latest_image: Image.Image = None - self._latest_image_id: int = None - self._camera_feed_task: asyncio.Task = None - self._enabled = False - - @property - @util.block_while_none() - def latest_image(self) -> Image.Image: - """:class:`Image.Image`: The most recently processed image received from the robot. - - :getter: Returns the Pillow Image representing the latest image - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_camera_feed=True) as robot: - image = robot.camera.latest_image - image.show() - """ - if not self._camera_feed_task: - raise Exception("Camera feed not open!") # TODO: Use a VectorException - return self._latest_image - - @property - @util.block_while_none() - def latest_image_id(self) -> int: - """The most recently processed image's id received from the robot. - - Used only to track chunks of the same image. - - :getter: Returns the id for the latest image - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_camera_feed=True) as robot: - image = robot.camera.latest_image - image.show() - print(f"latest_image_id: {robot.camera.latest_image_id}") - """ - if not self._camera_feed_task: - raise Exception("Camera feed not open!") # TODO: Use a VectorException - return self._latest_image_id - - def init_camera_feed(self) -> None: - """Begin camera feed task.""" - if not self._camera_feed_task or self._camera_feed_task.done(): - self._enabled = True - self._camera_feed_task = self.conn.loop.create_task(self._request_and_handle_images()) - - def close_camera_feed(self) -> None: - """Cancel camera feed task.""" - if self._camera_feed_task: - self._enabled = False - self._camera_feed_task.cancel() - future = self.conn.run_coroutine(self._camera_feed_task) - future.result() - self._camera_feed_task = None - - def _unpack_image(self, msg: protocol.CameraFeedResponse) -> None: - """Processes raw data from the robot into a more more useful image structure.""" - size = len(msg.data) - - # Constuct numpy array out of source data - array = np.empty(size, dtype=np.uint8) - array[0:size] = list(msg.data) - - # Decode compressed source data into uncompressed image data - imageArray = cv2.imdecode(array, -1) - imageArray = cv2.cvtColor(imageArray, cv2.COLOR_BGR2RGB) - - # Convert to Pillow Image - self._latest_image = Image.fromarray(imageArray) - self._latest_image_id = msg.image_id - self.robot.viewer.enqueue_frame(self._latest_image) - - async def _request_and_handle_images(self) -> None: - """Queries and listens for camera feed events from the robot. - Received events are parsed by a helper function.""" - try: - req = protocol.CameraFeedRequest() - async for evt in self.grpc_interface.CameraFeed(req): - # If the camera feed is disabled after stream is setup, exit the stream - # (the camera feed on the robot is disabled internally on stream exit) - if not self._enabled: - self.logger.warning('Camera feed has been disabled. Enable the feed to start/continue receiving camera feed data') - return - self._unpack_image(evt) - except CancelledError: - self.logger.debug('Camera feed task was cancelled. This is expected during disconnection.') diff --git a/anki_vector/color.py b/anki_vector/color.py deleted file mode 100755 index c492339..0000000 --- a/anki_vector/color.py +++ /dev/null @@ -1,88 +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 -# -# https://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. - -""" -Colors to be used with a light or Vector's screen. -""" - - -class Color: - """A Color to be used with a Light or Vector's screen. - - Either int_color or rgb may be used to specify the actual color. - Any alpha components (from int_color) are ignored - all colors are fully opaque. - - :param int_color: A 32 bit value holding the binary RGBA value (where A - is ignored and forced to be fully opaque). - :param rgb: A tuple holding the integer values from 0-255 for (reg, green, blue) - :param name: A name to assign to this color. - """ - - def __init__(self, int_color: int = None, rgb: tuple = None, name: str = None): - self.name = name - self._int_color = 0 - if int_color is not None: - self._int_color = int_color | 0xff - elif rgb is not None: - self._int_color = (rgb[0] << 24) | (rgb[1] << 16) | (rgb[2] << 8) | 0xff - - @property - def int_color(self) -> int: - """The encoded integer value of the color.""" - return self._int_color - - @property - def rgb565_bytepair(self): - """bytes[]: Two bytes representing an int16 color with rgb565 encoding. - - This format reflects the robot's Screen color range, and performing this - conversion will reduce network traffic when sending Screen data. - """ - - red5 = ((self._int_color >> 24) & 0xff) >> 3 - green6 = ((self._int_color >> 16) & 0xff) >> 2 - blue5 = ((self._int_color >> 8) & 0xff) >> 3 - - green3_hi = green6 >> 3 - green3_low = green6 & 0x07 - - int_565_color_lowbyte = (green3_low << 5) | blue5 - int_565_color_highbyte = (red5 << 3) | green3_hi - - return [int_565_color_highbyte, int_565_color_lowbyte] - - -#: :class:`Color`: Green color instance. -green = Color(name="green", int_color=0x00ff00ff) - -#: :class:`Color`: Red color instance. -red = Color(name="red", int_color=0xff0000ff) - -#: :class:`Color`: Blue color instance. -blue = Color(name="blue", int_color=0x0000ffff) - -#: :class:`Color`: Cyan color instance. -cyan = Color(name="cyan", int_color=0x00ffffff) - -#: :class:`Color`: Magenta color instance. -magenta = Color(name="magenta", int_color=0xff00ffff) - -#: :class:`Color`: Yellow color instance. -yellow = Color(name="yellow", int_color=0xffff00ff) - -#: :class:`Color`: White color instance. -white = Color(name="white", int_color=0xffffffff) - -#: :class:`Color`: Instance representing no color (i.e., lights off). -off = Color(name="off") diff --git a/anki_vector/configure/__main__.py b/anki_vector/configure/__main__.py deleted file mode 100755 index 41c08cf..0000000 --- a/anki_vector/configure/__main__.py +++ /dev/null @@ -1,322 +0,0 @@ -#!/usr/bin/env python3 - -""" -***Anki Vector Python SDK Setup*** - -Vector requires all requests be authorized by an authenticated Anki user. - -This script will enable this device to authenticate with your Vector -robot for use with a Vector Python SDK program. - -Vector must be powered on and connected on the same network as your -computer. 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). - -See the README for more information. - -Use of Vector and the Vector SDK is subject to Anki's Privacy Policy and Terms and Conditions. - -https://www.anki.com/en-us/company/privacy -https://www.anki.com/en-us/company/terms-and-conditions - -""" - -import argparse -import configparser -from getpass import getpass -import json -import os -from pathlib import Path -import re -import socket -import sys - -from cryptography import x509 -from cryptography.hazmat.backends import default_backend -import grpc -import requests -try: - from termcolor import colored # pylint: disable=import-error -except: # pylint: disable=bare-except - def colored(text, color=None, on_color=None, attrs=None): # pylint: disable=unused-argument - return text - -import anki_vector -from anki_vector import messaging - - -class ApiHandler: - def __init__(self, headers: dict, url: str): - self._headers = headers - self._url = url - - @property - def headers(self): - return self._headers - - @property - def url(self): - return self._url - - -class Api: - def __init__(self): - self._handler = ApiHandler( - headers={ - 'User-Agent': f'Vector-sdk/{anki_vector.__version__}', - 'Anki-App-Key': 'aung2ieCho3aiph7Een3Ei' - }, - url='https://accounts.api.anki.com/1/sessions' - ) - - @property - def name(self): - return "Anki Cloud" - - @property - def handler(self): - return self._handler - - -def get_serial(serial=None): - if not serial: - serial = os.environ.get('ANKI_ROBOT_SERIAL') - if not serial: - print("\n\nPlease find your robot serial number (ex. 00e20100) located on the underside of Vector, or accessible from Vector's debug screen.") - serial = input('Enter robot serial number: ') - else: - print("Found robot serial number in environment variable '{}'".format(colored("ANKI_ROBOT_SERIAL", "green"))) - serial = serial.lower() - print("Using robot serial number: {}".format(colored(serial, "cyan"))) - return serial - - -def get_cert(serial=None): - serial = get_serial(serial) - print("\nDownloading Vector certificate from Anki...", end="") - sys.stdout.flush() - r = requests.get('https://session-certs.token.global.anki-services.com/vic/{}'.format(serial)) - if r.status_code != 200: - print(colored(" ERROR", "red")) - sys.exit(r.content) - print(colored(" DONE", "green")) - cert = r.content - return cert, serial - - -def user_authentication(session_id: bytes, cert: bytes, ip: str, name: str) -> str: - # Pin the robot certificate for opening the channel - creds = grpc.ssl_channel_credentials(root_certificates=cert) - - print("Attempting to download guid from {} at {}:443...".format(colored(name, "cyan"), colored(ip, "cyan")), end="") - sys.stdout.flush() - channel = grpc.secure_channel("{}:443".format(ip), creds, - options=(("grpc.ssl_target_name_override", name,),)) - - # Verify the connection to Vector is able to be established (client-side) - try: - # Explicitly grab _channel._channel to test the underlying grpc channel directly - grpc.channel_ready_future(channel).result(timeout=15) - except grpc.FutureTimeoutError: - print(colored(" ERROR", "red")) - sys.exit("\nUnable to connect to Vector\n" - "Please be sure to connect via the Vector companion app first, and connect your computer to the same network as your Vector.") - - try: - interface = messaging.client.ExternalInterfaceStub(channel) - request = messaging.protocol.UserAuthenticationRequest( - user_session_id=session_id.encode('utf-8'), - client_name=socket.gethostname().encode('utf-8')) - response = interface.UserAuthentication(request) - if response.code != messaging.protocol.UserAuthenticationResponse.AUTHORIZED: # pylint: disable=no-member - print(colored(" ERROR", "red")) - sys.exit("\nFailed to authorize request:\n" - "Please be sure to first set up Vector using the companion app.") - except grpc.RpcError as e: - print(colored(" ERROR", "red")) - sys.exit("\nFailed to authorize request:\n" - "An unknown error occurred '{}'".format(e)) - - print(colored(" DONE\n", "green")) - return response.client_token_guid - - -def get_session_token(api, username=None): - print("Enter your email and password. Make sure to use the same account that was used to set up your Vector.") - if not username: - username = input("Enter Email: ") - else: - print("Using email from command line: {}".format(colored(username, "cyan"))) - password = getpass("Enter Password: ") - payload = {'username': username, 'password': password} - - print("\nAuthenticating with {}...".format(api.name), end="") - sys.stdout.flush() - r = requests.post(api.handler.url, data=payload, headers=api.handler.headers) - if r.status_code != 200: - print(colored(" ERROR", "red")) - sys.exit(r.content) - print(colored(" DONE\n", "green")) - return json.loads(r.content) - - -def standardize_name(robot_name): - # Extend the name if not enough is provided - if len(robot_name) == 4: - robot_name = "Vector-{}".format(robot_name.upper()) - # Fix possible capitalization and space/dash/etc. - if re.match("[Vv]ector.[A-Za-z0-9]{4}", robot_name): - robot_name = "V{}-{}".format(robot_name[1:-5], robot_name[-4:].upper()) - # Check that the end is valid - if re.match("Vector-[A-Z0-9]{4}", robot_name): - return robot_name - print(colored(" ERROR", "red")) - sys.exit("Invalid robot name. Please match the format exactly. Example: Vector-A1B2") - - -def get_name_and_ip(robot_name=None, ip=None): - if not robot_name: - robot_name = os.environ.get('VECTOR_ROBOT_NAME') - if not robot_name: - print("\n\nFind your robot name (ex. Vector-A1B2) by placing Vector on the charger and double-clicking Vector's backpack button.") - robot_name = input("Enter robot name: ") - else: - print("Found robot name in environment variable '{}'".format(colored("VECTOR_ROBOT_NAME", "green"))) - robot_name = standardize_name(robot_name) - print("Using robot name: {}".format(colored(robot_name, "cyan"))) - if not ip: - ip = os.environ.get('ANKI_ROBOT_HOST') - if not ip: - print("\n\nFind your robot ip address (ex. 192.168.42.42) by placing Vector on the charger, double-clicking Vector's backpack button,\n" - "then raising and lowering his arms. If you see {} on his face, reconnect Vector to your WiFi using the Vector Companion App.".format(colored("XX.XX.XX.XX", "red"))) - ip = input("Enter robot ip: ") - else: - print("Found robot ip address in environment variable '{}'".format(colored("ANKI_ROBOT_HOST", "green"))) - print("Using IP: {}".format(colored(ip, "cyan"))) - return robot_name, ip - - -def save_cert(cert, name, serial, anki_dir): - """Write Vector's certificate to a file located in the user's home directory""" - os.makedirs(str(anki_dir), exist_ok=True) - cert_file = str(anki_dir / "{name}-{serial}.cert".format(name=name, serial=serial)) - print("Writing certificate file to '{}'...\n".format(colored(cert_file, "cyan"))) - with os.fdopen(os.open(cert_file, os.O_WRONLY | os.O_CREAT, 0o600), 'wb') as f: - f.write(cert) - return cert_file - - -def validate_cert_name(cert_file, robot_name): - """Validate the name on Vector's certificate against the user-provided name""" - with open(cert_file, "rb") as f: - cert_file = f.read() - cert = x509.load_pem_x509_certificate(cert_file, default_backend()) - for fields in cert.subject: - current = str(fields.oid) - if "commonName" in current: - common_name = fields.value - if common_name != robot_name: - print(colored(" ERROR", "red")) - sys.exit("The name of the certificate ({}) does not match the name provided ({}).\n" - "Please verify the name, and try again.".format(common_name, robot_name)) - else: - return - - -def write_config(serial, cert_file=None, ip=None, name=None, guid=None, clear=True): - home = Path.home() - config_file = str(home / ".anki_vector" / "sdk_config.ini") - print("Writing config file to '{}'...".format(colored(config_file, "cyan"))) - - config = configparser.ConfigParser(strict=False) - - try: - config.read(config_file) - except configparser.ParsingError: - if os.path.exists(config_file): - os.rename(config_file, config_file + "-error") - if clear: - config[serial] = {} - if cert_file: - config[serial]["cert"] = cert_file - if ip: - config[serial]["ip"] = ip - if name: - config[serial]["name"] = name - if guid: - config[serial]["guid"] = guid.decode("utf-8") - temp_file = config_file + "-temp" - if os.path.exists(config_file): - os.rename(config_file, temp_file) - try: - with os.fdopen(os.open(config_file, os.O_WRONLY | os.O_CREAT, 0o600), 'w') as f: - config.write(f) - except Exception as e: - if os.path.exists(temp_file): - os.rename(temp_file, config_file) - raise e - else: - if os.path.exists(temp_file): - os.remove(temp_file) - - -def main(api): - parser = argparse.ArgumentParser(description=("Vector requires all requests be authorized by an authenticated Anki user. " - "This script will enable this device to authenticate with your Vector " - "robot for use with a Vector Python SDK program."), - epilog=("See the README for more information. " - "Use of Vector and the Vector SDK is subject to Anki's Privacy Policy and Terms and Conditions. " - "https://www.anki.com/en-us/company/privacy and " - "https://www.anki.com/en-us/company/terms-and-conditions")) - parser.add_argument("-e", "--email", help="The email used by your Anki account.") - parser.add_argument("-i", "--ip", help=("Your robot ip address (ex. 192.168.42.42). " - "It may be found by placing Vector on the charger, " - "double-clicking Vector's backpack button, " - "then raising and lowering his arms. " - "If you see {} on his face, " - "reconnect Vector to your WiFi using the Vector Companion App.".format(colored("XX.XX.XX.XX", "red")))) - parser.add_argument("-n", "--name", help=("Your robot name (ex. Vector-A1B2). " - "It may be found by placing Vector on the charger and double-clicking Vector's backpack button.")) - parser.add_argument("-s", "--serial", help=("Your robot serial number (ex. 00e20100). " - "It is located on the underside of Vector, or accessible from Vector's debug screen.")) - parser.add_argument("-u", "--update", dest="new_ip", help=("Update the stored ip for Vector. This makes it easier to transfer between networks.")) - args = parser.parse_args() - - if args.new_ip: - serial = get_serial(args.serial) - write_config(serial, ip=args.new_ip, clear=False) - print(colored("\nIP Updated!", "green")) - sys.exit() - - print(__doc__) - - valid = ["y", "Y", "yes", "YES"] - environ = input("Do you wish to proceed? (y/n) ") - if environ not in valid: - sys.exit("Stopping...") - - name, ip = get_name_and_ip(args.name, args.ip) - cert, serial = get_cert(args.serial) - - home = Path.home() - anki_dir = home / ".anki_vector" - - cert_file = save_cert(cert, name, serial, anki_dir) - validate_cert_name(cert_file, name) - - token = get_session_token(api, args.email) - if not token.get("session"): - sys.exit("Session error: {}".format(token)) - - guid = user_authentication(token["session"]["session_token"], cert, ip, name) - - # Store credentials in the .anki_vector directory's sdk_config.ini file - write_config(serial, cert_file, ip, name, guid) - print(colored("\nSUCCESS!", "green")) - - -if __name__ == "__main__": - main(Api()) diff --git a/anki_vector/connection.py b/anki_vector/connection.py deleted file mode 100644 index abcb12f..0000000 --- a/anki_vector/connection.py +++ /dev/null @@ -1,757 +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 -# -# https://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. - -""" -Management of the connection to and from Vector. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['CONTROL_PRIORITY_LEVEL', 'Connection', 'on_connection_thread'] - -import asyncio -from concurrent import futures -from enum import Enum -import functools -import inspect -import logging -import platform -import sys -import threading -from typing import Any, Awaitable, Callable, Coroutine, Dict, List - -import grpc -import aiogrpc - -from . import util -from .exceptions import (connection_error, - VectorControlException, - VectorControlTimeoutException, - VectorInvalidVersionException, - VectorNotFoundException) -from .messaging import client, protocol -from .version import __version__ - -CLIENT_VERSION = 2 -MIN_HOST_VERSION = 0 - - -class CONTROL_PRIORITY_LEVEL(Enum): - """Enum used to specify the priority level for the program.""" - - #: Runs below Mandatory Physical Reactions such as tucking Vector's head and arms during a fall, - #: yet above Trigger-Word Detection. - TOP_PRIORITY_AI = protocol.ControlRequest.TOP_PRIORITY_AI # pylint: disable=no-member - - -class _ControlEventManager: - """This manages every :class:`asyncio.Event` that handles the behavior control - system. - - These include three events: granted, lost, and request. - - :class:`granted_event` represents the behavior system handing control to the SDK. - - :class:`lost_event` represents a higher priority behavior taking control away from the SDK. - - :class:`request_event` Is a way of alerting :class:`Connection` to request control. - """ - - def __init__(self, loop: asyncio.BaseEventLoop = None, priority: CONTROL_PRIORITY_LEVEL = None): - self._granted_event = asyncio.Event(loop=loop) - self._lost_event = asyncio.Event(loop=loop) - self._request_event = asyncio.Event(loop=loop) - self._has_control = False - self._priority = priority - self._is_shutdown = False - - @property - def granted_event(self) -> asyncio.Event: - """This event is used to notify listeners that control has been granted to the SDK.""" - return self._granted_event - - @property - def lost_event(self) -> asyncio.Event: - """Represents a higher priority behavior taking control away from the SDK.""" - return self._lost_event - - @property - def request_event(self) -> asyncio.Event: - """Used to alert :class:`Connection` to request control.""" - return self._request_event - - @property - def has_control(self) -> bool: - """Check to see that the behavior system has control (without blocking by checking :class:`granted_event`)""" - return self._has_control - - @property - def priority(self) -> CONTROL_PRIORITY_LEVEL: - """The currently desired priority for the SDK.""" - return self._priority - - @property - def is_shutdown(self) -> bool: - """Detect if the behavior control stream is supposed to shut down.""" - return self._is_shutdown - - def request(self, priority: CONTROL_PRIORITY_LEVEL = CONTROL_PRIORITY_LEVEL.TOP_PRIORITY_AI) -> None: - """Tell the behavior stream to request control via setting the :class:`request_event`. - - This will signal Connection's :func:`_request_handler` generator to send a request control message on the BehaviorControl stream. - This signal happens asynchronously, and can be tracked using the :class:`granted_event` parameter. - - :param priority: The level of control in the behavior system. This determines which actions are allowed to - interrupt the SDK execution. See :class:`CONTROL_PRIORITY_LEVEL` for more information. - """ - if priority is None: - raise Exception("Must provide a priority level to request. To disable control, use :func:`release()`.") - self._priority = priority - self._request_event.set() - - def release(self) -> None: - """Tell the behavior stream to release control via setting the :class:`request_event` while priority is ``None``. - - This will signal Connection's :func:`_request_handler` generator to send a release control message on the BehaviorControl stream. - This signal happens asynchronously, and can be tracked using the :class:`lost_event` parameter. - """ - self._priority = None - self._request_event.set() - - def update(self, enabled: bool) -> None: - """Update the current state of control (either enabled or disabled) - - :param enabled: Used to enable/disable behavior control - """ - self._has_control = enabled - if enabled: - self._granted_event.set() - self._lost_event.clear() - else: - self._lost_event.set() - self._granted_event.clear() - - def shutdown(self) -> None: - """Tells the control stream to shut down. - - This will return control to the rest of the behavior system. - """ - self._has_control = False - self._granted_event.set() - self._lost_event.set() - self._is_shutdown = True - self._request_event.set() - - -class Connection: - """Creates and maintains a aiogrpc connection including managing the connection thread. - The connection thread decouples the actual messaging layer from the user's main thread, - and requires any network requests to be ran using :func:`asyncio.run_coroutine_threadsafe` - to make them run on the other thread. Connection provides two helper functions for running - a function on the connection thread: :func:`~Connection.run_coroutine` and - :func:`~Connection.run_soon`. - - This class may be used to bypass the structures of the python sdk handled by - :class:`~anki_vector.robot.Robot`, and instead talk to aiogrpc more directly. - - The values for the cert_file location and the guid can be found in your home directory in - the sdk_config.ini file. - - .. code-block:: python - - import anki_vector - - # Connect to your Vector - conn = anki_vector.connection.Connection("Vector-XXXX", "XX.XX.XX.XX:443", "/path/to/file.cert", "") - conn.connect() - # Run your commands - async def play_animation(): - # Run your commands - anim = anki_vector.messaging.protocol.Animation(name="anim_pounce_success_02") - anim_request = anki_vector.messaging.protocol.PlayAnimationRequest(animation=anim) - return await conn.grpc_interface.PlayAnimation(anim_request) # This needs to be run in an asyncio loop - conn.run_coroutine(play_animation()).result() - # Close the connection - conn.close() - - :param name: Vector's name in the format of "Vector-XXXX". - :param host: The IP address and port of Vector in the format "XX.XX.XX.XX:443". - :param cert_file: The location of the certificate file on disk. - :param guid: Your robot's unique secret key. - :param requires_behavior_control: True if the connection requires behavior control. - """ - - def __init__(self, name: str, host: str, cert_file: str, guid: str, requires_behavior_control: bool = True): - if cert_file is None: - raise Exception("Must provide a cert file") - self._loop: asyncio.BaseEventLoop = None - self.name = name - self.host = host - self.cert_file = cert_file - self._interface = None - self._channel = None - self._has_control = False - self._logger = util.get_class_logger(__name__, self) - self._control_stream_task = None - self._control_events: _ControlEventManager = None - self._guid = guid - self._thread: threading.Thread = None - self._ready_signal: threading.Event = threading.Event() - self._done_signal: asyncio.Event = None - self._conn_exception = False - self._requires_behavior_control = requires_behavior_control - self.active_commands = [] - - @property - def loop(self) -> asyncio.BaseEventLoop: - """A direct reference to the loop on the connection thread. - Can be used to run functions in on thread. - - .. testcode:: - - import anki_vector - import asyncio - - async def connection_function(): - print("I'm running in the connection thread event loop.") - - with anki_vector.Robot() as robot: - asyncio.run_coroutine_threadsafe(connection_function(), robot.conn.loop) - - :returns: The loop running inside the connection thread - """ - if self._loop is None: - raise Exception("Attempted to access the connection loop before it was ready") - return self._loop - - @property - def thread(self) -> threading.Thread: - """A direct reference to the connection thread. Available to callers to determine if the - current thread is the connection thread. - - .. testcode:: - - import anki_vector - import threading - - with anki_vector.Robot() as robot: - if threading.current_thread() is robot.conn.thread: - print("This code is running on the connection thread") - else: - print("This code is not running on the connection thread") - - :returns: The connection thread where all of the grpc messages are being processed. - """ - if self._thread is None: - raise Exception("Attempted to access the connection loop before it was ready") - return self._thread - - @property - def grpc_interface(self) -> client.ExternalInterfaceStub: - """A direct reference to the connected aiogrpc interface. - - This may be used to directly call grpc messages bypassing :class:`anki_vector.Robot` - - .. code-block:: python - - import anki_vector - - # Connect to your Vector - conn = anki_vector.connection.Connection("Vector-XXXX", "XX.XX.XX.XX:443", "/path/to/file.cert", "") - conn.connect() - # Run your commands - async def play_animation(): - # Run your commands - anim = anki_vector.messaging.protocol.Animation(name="anim_pounce_success_02") - anim_request = anki_vector.messaging.protocol.PlayAnimationRequest(animation=anim) - return await conn.grpc_interface.PlayAnimation(anim_request) # This needs to be run in an asyncio loop - conn.run_coroutine(play_animation()).result() - # Close the connection - conn.close() - """ - return self._interface - - @property - def requires_behavior_control(self) -> bool: - """True if the :class:`Connection` requires behavior control. - - To be able to directly control Vector's motors, override his screen, play an animation, etc., - the :class:`Connection` will need behavior control. This boolean signifies that - the :class:`Connection` will try to maintain control of Vector's behavior system even after losing - control to higher priority robot behaviors such as returning home to charge a low battery. - - For more information about behavior control, see :ref:`behavior `. - - .. code-block:: python - - import time - - import anki_vector - - with anki_vector.Robot(requires_behavior_control=False) as robot: - async def callback(event_type, event): - await robot.conn.request_control() - print(robot.conn.requires_behavior_control) # Will print True - await robot.anim.play_animation('anim_pounce_success_02') - await robot.conn.release_control() - - print(robot.conn.requires_behavior_control) # Will print False - robot.events.subscribe(callback, anki_vector.events.Events.robot_observed_face) - - # Waits 10 seconds. Show Vector your face. - time.sleep(10) - """ - return self._requires_behavior_control - - @property - def control_lost_event(self) -> asyncio.Event: - """This provides an :class:`asyncio.Event` that a user may :func:`wait()` upon to - detect when Vector has taken control of the behavior system at a higher priority. - - .. testcode:: - - import anki_vector - - async def auto_reconnect(conn: anki_vector.connection.Connection): - await conn.control_lost_event.wait() - conn.request_control() - """ - return self._control_events.lost_event - - @property - def control_granted_event(self) -> asyncio.Event: - """This provides an :class:`asyncio.Event` that a user may :func:`wait()` upon to - detect when Vector has given control of the behavior system to the SDK program. - - .. testcode:: - - import anki_vector - - async def wait_for_control(conn: anki_vector.connection.Connection): - await conn.control_granted_event.wait() - # Run commands that require behavior control - """ - return self._control_events.granted_event - - def request_control(self, timeout: float = 10.0): - """Explicitly request behavior control. Typically used after detecting :func:`control_lost_event`. - - To be able to directly control Vector's motors, override his screen, play an animation, etc., - the :class:`Connection` will need behavior control. This function will acquire control - of Vector's behavior system. This will raise a :class:`VectorControlTimeoutException` if it fails - to gain control before the timeout. - - For more information about behavior control, see :ref:`behavior ` - - .. testcode:: - - import anki_vector - - async def auto_reconnect(conn: anki_vector.connection.Connection): - await conn.control_lost_event.wait() - conn.request_control(timeout=5.0) - - :param timeout: The time allotted to attempt a connection, in seconds. - """ - if self._thread is threading.current_thread(): - return asyncio.ensure_future(self._request_control(timeout=timeout), loop=self._loop) - return self.run_coroutine(self._request_control(timeout=timeout)) - - async def _request_control(self, timeout: float = 10.0): - self._requires_behavior_control = True - self._control_events.request() - try: - self._has_control = await asyncio.wait_for(self.control_granted_event.wait(), timeout) - except futures.TimeoutError as e: - raise VectorControlTimeoutException(f"Surpassed timeout of {timeout}s") from e - - def release_control(self, timeout: float = 10.0): - """Explicitly request control. Typically used after detecting :func:`control_lost_event`. - - To be able to directly control Vector's motors, override his screen, play an animation, etc., - the :class:`Connection` will need behavior control. This function will acquire control - of Vector's behavior system. This will raise a :class:`VectorControlTimeoutException` if it fails - to receive a control_lost event before the timeout. - - .. testcode:: - - import anki_vector - - async def wait_for_control(conn: anki_vector.connection.Connection): - await conn.control_granted_event.wait() - # Run commands that require behavior control - conn.release_control() - - :param timeout: The time allotted to attempt to release control, in seconds. - """ - if self._thread is threading.current_thread(): - return asyncio.ensure_future(self._release_control(timeout=timeout), loop=self._loop) - return self.run_coroutine(self._release_control(timeout=timeout)) - - async def _release_control(self, timeout: float = 10.0): - self._requires_behavior_control = False - self._control_events.release() - try: - self._has_control = await asyncio.wait_for(self.control_lost_event.wait(), timeout) - except futures.TimeoutError as e: - raise VectorControlTimeoutException(f"Surpassed timeout of {timeout}s") from e - - def connect(self, timeout: float = 10.0) -> None: - """Connect to Vector. This will start the connection thread which handles all messages - between Vector and Python. - - .. code-block:: python - - import anki_vector - - # Connect to your Vector - conn = anki_vector.connection.Connection("Vector-XXXX", "XX.XX.XX.XX:443", "/path/to/file.cert", "") - conn.connect() - # Run your commands - async def play_animation(): - # Run your commands - anim = anki_vector.messaging.protocol.Animation(name="anim_pounce_success_02") - anim_request = anki_vector.messaging.protocol.PlayAnimationRequest(animation=anim) - return await conn.grpc_interface.PlayAnimation(anim_request) # This needs to be run in an asyncio loop - conn.run_coroutine(play_animation()).result() - # Close the connection - conn.close() - - :param timeout: The time allotted to attempt a connection, in seconds. - """ - if self._thread: - raise Exception("\n\nRepeated connections made to open Connection.") - self._ready_signal.clear() - self._thread = threading.Thread(target=self._connect, args=(timeout,), daemon=True, name="gRPC Connection Handler Thread") - self._thread.start() - ready = self._ready_signal.wait(timeout=2 * timeout) - if not ready: - raise VectorNotFoundException() - if hasattr(self._ready_signal, "exception"): - e = getattr(self._ready_signal, "exception") - delattr(self._ready_signal, "exception") - raise e - - def _connect(self, timeout: float) -> None: - """The function that runs on the connection thread. This will connect to Vector, - and establish the BehaviorControl stream. - """ - try: - if threading.main_thread() is threading.current_thread(): - raise Exception("\n\nConnection._connect must be run outside of the main thread.") - self._loop = asyncio.new_event_loop() - asyncio.set_event_loop(self._loop) - self._done_signal = asyncio.Event() - if not self._requires_behavior_control: - self._control_events = _ControlEventManager(self._loop) - else: - self._control_events = _ControlEventManager(self._loop, priority=CONTROL_PRIORITY_LEVEL.TOP_PRIORITY_AI) - trusted_certs = None - with open(self.cert_file, 'rb') as cert: - trusted_certs = cert.read() - - # Pin the robot certificate for opening the channel - channel_credentials = aiogrpc.ssl_channel_credentials(root_certificates=trusted_certs) - # Add authorization header for all the calls - call_credentials = aiogrpc.access_token_call_credentials(self._guid) - - credentials = aiogrpc.composite_channel_credentials(channel_credentials, call_credentials) - - self._logger.info(f"Connecting to {self.host} for {self.name} using {self.cert_file}") - self._channel = aiogrpc.secure_channel(self.host, credentials, - options=(("grpc.ssl_target_name_override", self.name,),)) - - # Verify the connection to Vector is able to be established (client-side) - try: - # Explicitly grab _channel._channel to test the underlying grpc channel directly - grpc.channel_ready_future(self._channel._channel).result(timeout=timeout) # pylint: disable=protected-access - except grpc.FutureTimeoutError as e: - raise VectorNotFoundException() from e - - self._interface = client.ExternalInterfaceStub(self._channel) - - # Verify Vector and the SDK have compatible protocol versions - version = protocol.ProtocolVersionRequest(client_version=CLIENT_VERSION, min_host_version=MIN_HOST_VERSION) - protocol_version = self._loop.run_until_complete(self._interface.ProtocolVersion(version)) - if protocol_version.result != protocol.ProtocolVersionResponse.SUCCESS or MIN_HOST_VERSION > protocol_version.host_version: # pylint: disable=no-member - raise VectorInvalidVersionException(version, protocol_version) - - self._control_stream_task = self._loop.create_task(self._open_connections()) - - # Initialze SDK - sdk_module_version = __version__ - python_version = platform.python_version() - python_implementation = platform.python_implementation() - os_version = platform.platform() - cpu_version = platform.machine() - initialize = protocol.SDKInitializationRequest(sdk_module_version=sdk_module_version, - python_version=python_version, - python_implementation=python_implementation, - os_version=os_version, - cpu_version=cpu_version) - self._loop.run_until_complete(self._interface.SDKInitialization(initialize)) - - if self._requires_behavior_control: - self._loop.run_until_complete(self._request_control(timeout=timeout)) - except Exception as e: # pylint: disable=broad-except - # Propagate the errors to the calling thread - setattr(self._ready_signal, "exception", e) - self._loop.close() - return - finally: - self._ready_signal.set() - - try: - async def wait_until_done(): - return await self._done_signal.wait() - self._loop.run_until_complete(wait_until_done()) - finally: - self._loop.close() - - async def _request_handler(self): - """Handles generating messages for the BehaviorControl stream.""" - while await self._control_events.request_event.wait(): - self._control_events.request_event.clear() - if self._control_events.is_shutdown: - return - priority = self._control_events.priority - if priority is None: - msg = protocol.ControlRelease() - msg = protocol.BehaviorControlRequest(control_release=msg) - else: - msg = protocol.ControlRequest(priority=priority.value) - msg = protocol.BehaviorControlRequest(control_request=msg) - self._logger.debug(f"Sending: {msg}") - yield msg - - async def _open_connections(self): - """Starts the BehaviorControl stream, and handles the messages coming back from the robot.""" - try: - async for response in self._interface.BehaviorControl(self._request_handler()): - response_type = response.WhichOneof("response_type") - if response_type == 'control_granted_response': - self._logger.info(response) - self._control_events.update(True) - elif response_type == 'control_lost_event': - self._cancel_active() - self._logger.info(response) - self._control_events.update(False) - except futures.CancelledError: - self._logger.debug('Behavior handler task was cancelled. This is expected during disconnection.') - - def _cancel_active(self): - for fut in self.active_commands: - if not fut.done(): - fut.cancel() - self.active_commands = [] - - def close(self): - """Cleanup the connection, and shutdown all the even handlers. - - Usually this should be invoked by the Robot class when it closes. - - .. code-block:: python - - import anki_vector - - # Connect to your Vector - conn = anki_vector.connection.Connection("Vector-XXXX", "XX.XX.XX.XX:443", "/path/to/file.cert", "") - conn.connect() - # Run your commands - async def play_animation(): - # Run your commands - anim = anki_vector.messaging.protocol.Animation(name="anim_pounce_success_02") - anim_request = anki_vector.messaging.protocol.PlayAnimationRequest(animation=anim) - return await conn.grpc_interface.PlayAnimation(anim_request) # This needs to be run in an asyncio loop - conn.run_coroutine(play_animation()).result() - # Close the connection - conn.close() - """ - if self._control_events: - self._control_events.shutdown() - if self._control_stream_task: - self._control_stream_task.cancel() - self.run_coroutine(self._control_stream_task).result() - self._cancel_active() - if self._channel: - self.run_coroutine(self._channel.close()).result() - self.run_coroutine(self._done_signal.set) - self._thread.join(timeout=5) - self._thread = None - - def run_soon(self, coro: Awaitable) -> None: - """Schedules the given awaitable to run on the event loop for the connection thread. - - .. testcode:: - - import anki_vector - import time - - async def my_coroutine(): - print("Running on the connection thread") - - with anki_vector.Robot() as robot: - robot.conn.run_soon(my_coroutine()) - time.sleep(1) - - :param coro: The coroutine, task or any awaitable to schedule for execution on the connection thread. - """ - if coro is None or not inspect.isawaitable(coro): - raise Exception(f"\n\n{coro.__name__ if hasattr(coro, '__name__') else coro} is not awaitable, so cannot be ran with run_soon.\n") - - def soon(): - try: - asyncio.ensure_future(coro) - except TypeError as e: - raise Exception(f"\n\n{coro.__name__ if hasattr(coro, '__name__') else coro} could not be ensured as a future.\n") from e - if threading.current_thread() is self._thread: - self._loop.call_soon(soon) - else: - self._loop.call_soon_threadsafe(soon) - - def run_coroutine(self, coro: Awaitable) -> Any: - """Runs a given awaitable on the connection thread's event loop. - Cannot be called from within the connection thread. - - .. testcode:: - - import anki_vector - - async def my_coroutine(): - print("Running on the connection thread") - return "Finished" - - with anki_vector.Robot() as robot: - result = robot.conn.run_coroutine(my_coroutine()) - - :param coro: The coroutine, task or any other awaitable which should be executed. - :returns: The result of the awaitable's execution. - """ - if threading.current_thread() is self._thread: - raise Exception("Attempting to invoke async from same thread." - "Instead you may want to use 'run_soon'") - if asyncio.iscoroutinefunction(coro) or asyncio.iscoroutine(coro): - return self._run_coroutine(coro) - if asyncio.isfuture(coro): - async def future_coro(): - return await coro - return self._run_coroutine(future_coro()) - if callable(coro): - async def wrapped_coro(): - return coro() - return self._run_coroutine(wrapped_coro()) - raise Exception("\n\nInvalid parameter to run_coroutine: {}\n" - "This function expects a coroutine, task, or awaitable.".format(type(coro))) - - def _run_coroutine(self, coro): - return asyncio.run_coroutine_threadsafe(coro, self._loop) - - -def on_connection_thread(log_messaging: bool = True, requires_control: bool = True) -> Callable[[Coroutine[util.Component, Any, None]], Any]: - """A decorator generator used internally to denote which functions will run on - the connection thread. This unblocks the caller of the wrapped function - and allows them to continue running while the messages are being processed. - - .. code-block:: python - - import anki_vector - - class MyComponent(anki_vector.util.Component): - @connection._on_connection_thread() - async def on_connection_thread(self): - # Do work on the connection thread - - :param log_messaging: True if the log output should include the entire message or just the size. Recommended for - large binary return values. - :param requires_control: True if the function should wait until behavior control is granted before executing. - :returns: A decorator which has 3 possible returns based on context: the result of the decorated function, - the :class:`concurrent.futures.Future` which points to the decorated function, or the - :class:`asyncio.Future` which points to the decorated function. - These contexts are: when the robot is a :class:`~anki_vector.robot.Robot`, - when the robot is an :class:`~anki_vector.robot.AsyncRobot`, and when - called from the connection thread respectively. - """ - def _on_connection_thread_decorator(func: Coroutine) -> Any: - """A decorator which specifies a function to be executed on the connection thread - - :params func: The function to be decorated - :returns: There are 3 possible returns based on context: the result of the decorated function, - the :class:`concurrent.futures.Future` which points to the decorated function, or the - :class:`asyncio.Future` which points to the decorated function. - These contexts are: when the robot is a :class:`anki_vector.robot.Robot`, - when the robot is an :class:`anki_vector.robot.AsyncRobot`, and when - called from the connection thread respectively. - """ - if not asyncio.iscoroutinefunction(func): - raise Exception("\n\nCannot define non-coroutine function '{}' to run on connection thread.\n" - "Make sure the function is defined using 'async def'.".format(func.__name__ if hasattr(func, "__name__") else func)) - - @functools.wraps(func) - async def log_handler(conn: Connection, func: Coroutine, logger: logging.Logger, *args: List[Any], **kwargs: Dict[str, Any]) -> Coroutine: - """Wrap the provided coroutine to better express exceptions as specific :class:`anki_vector.exceptions.VectorException`s, and - adds logging to incoming (from the robot) and outgoing (to the robot) messages. - """ - result = None - # TODO: only have the request wait for control if we're not done. If done raise an exception. - control = conn.control_granted_event - if requires_control and not control.is_set(): - if not conn.requires_behavior_control: - raise VectorControlException(func.__name__) - logger.info(f"Delaying {func.__name__} until behavior control is granted") - 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) - except grpc.RpcError as rpc_error: - raise connection_error(rpc_error) from rpc_error - logger.debug(f'Incoming {func.__name__}: {type(result).__name__} ({str(result).strip() if log_messaging else "size = {} bytes".format(sys.getsizeof(result))})') - return result - - @functools.wraps(func) - def result(*args: List[Any], **kwargs: Dict[str, Any]) -> Any: - """The function that is the result of the decorator. Provides a wrapped function. - - :param _return_future: A hidden parameter which allows the wrapped function to explicitly - return a future (default for AsyncRobot) or not (default for Robot). - :returns: Based on context this can return the result of the decorated function, - the :class:`concurrent.futures.Future` which points to the decorated function, or the - :class:`asyncio.Future` which points to the decorated function. - These contexts are: when the robot is a :class:`anki_vector.robot.Robot`, - when the robot is an :class:`anki_vector.robot.AsyncRobot`, and when - called from the connection thread respectively.""" - self = args[0] # Get the self reference from the function call - # if the call supplies a _return_future parameter then override force_async with that. - _return_future = kwargs.pop('_return_future', self.force_async) - wrapped_coroutine = log_handler(self.conn, func, self.logger, *args, **kwargs) - if threading.current_thread() == self.conn.thread: - if self.conn.loop.is_running(): - return asyncio.ensure_future(wrapped_coroutine, loop=self.conn.loop) - raise Exception("\n\nThe connection thread loop is not running, but a " - "function '{}' is being invoked on that thread.\n".format(func.__name__ if hasattr(func, "__name__") else func)) - future = asyncio.run_coroutine_threadsafe(wrapped_coroutine, self.conn.loop) - if requires_control: - self.conn.active_commands.append(future) - - def clear_when_done(fut): - if fut in self.conn.active_commands: - self.conn.active_commands.remove(fut) - future.add_done_callback(clear_when_done) - if _return_future: - return future - try: - return future.result() - except futures.CancelledError: - self.logger.warning(f"{func.__name__} cancelled because behavior control was lost") - return None - return result - return _on_connection_thread_decorator diff --git a/anki_vector/events.py b/anki_vector/events.py deleted file mode 100644 index 9c50a7d..0000000 --- a/anki_vector/events.py +++ /dev/null @@ -1,392 +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 -# -# https://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. - -""" -Event handler used to make functions subscribe to robot events. -""" - -__all__ = ['EventHandler', 'Events'] - -import asyncio -from concurrent.futures import CancelledError -from enum import Enum -import threading -from typing import Callable -import uuid - -from .connection import Connection -from . import util -from .messaging import protocol - - -class Events(Enum): - """List of events available.""" - - # Robot - robot_state = "robot_state" #: Robot event containing changes to the robot's state. - mirror_mode_disabled = "mirror_mode_disabled" # : Robot event triggered when mirror mode (camera feed displayed on robot's face) is automatically disabled due to SDK no longer having control of the robot. - vision_modes_auto_disabled = "vision_modes_auto_disabled" # : Robot event triggered when all vision modes are automatically disabled due to the SDK no longer having control of the robot. - - # Objects - object_available = "object_available" #: After the ConnectCube process is started, all available light cubes in range will broadcast an availability message through the Robot. - object_connection_state = "object_connection_state" # : Robot event for an object with the ability to connect to the robot digitally changing its connection state. - object_moved = "object_moved" #: Robot event triggered when an object starts moving. - object_stopped_moving = "object_stopped_moving" #: Robot event triggered when an object stops moving. - object_up_axis_changed = "object_up_axis_changed" #: Robot event triggered when an object's orientation changed. - object_tapped = "object_tapped" #: Robot event triggered when an object is tapped. - robot_observed_object = "robot_observed_object" #: Robot event triggered when an object is observed by the robot. - cube_connection_lost = "cube_connection_lost" #: Robot event triggered when an object's subscribed connection has been lost. - - robot_observed_face = "robot_observed_face" #: Robot event for when a face is observed by the robot. - robot_changed_observed_face_id = "robot_changed_observed_face_id" # : Robot event for when a known face changes its id. - - wake_word = "wake_word" #: Robot event triggered when Vector hears "Hey Vector" - - # Audio - audio_send_mode_changed = "audio_send_mode_changed" #: Robot event containing changes to the robot's audio stream source data processing mode. - - # Generated by SDK - object_observed = "object_observed" #: Python event triggered in response to robot_observed_object with sdk metadata. - object_appeared = "object_appeared" #: Python event triggered when an object first receives robot_observed_object. - object_disappeared = "object_disappeared" #: Python event triggered when an object has not received a robot_observed_object for a specified time. - object_finished_move = "object_finished_move" #: Python event triggered in response to object_stopped_moving with duration data. - 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.""" - - def __init__(self): - self.logger = util.get_class_logger(__name__, self) - self._conn = None - 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. - - :param connection: A reference to the connection from the SDK to the robot. - :param loop: The loop to run the event task on. - """ - self._conn = connection - self.listening_for_events = True - self._thread = threading.Thread(target=self._run_thread, daemon=True, name="Event Stream Handler Thread") - self._thread.start() - - def _run_thread(self): - try: - 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) - - async def wait_until_done(): - return await self._done_signal.wait() - self._loop.run_until_complete(wait_until_done()) - finally: - self._loop.close() - - def close(self): - """Stop listening for events. Automatically called by the :class:`anki_vector.robot.Robot` class. - """ - self.listening_for_events = False - try: - self.event_future.cancel() - self.event_future.result() - except CancelledError: - pass - self._loop.call_soon_threadsafe(self._done_signal.set) - self._thread.join(timeout=5) - self._thread = None - - 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. - - .. testcode:: - - 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') - robot.conn.run_coroutine(robot.events.dispatch_event_by_name('my_event dispatched', event_name='my_event')) - - :param event_data: Data to accompany the event. - :param event_name: The name of the event that will result in func being called. - """ - if not event_name: - self.logger.error('Bad event_name in dispatch_event.') - - if event_name in self.subscribers.keys(): - subscribers = self.subscribers[event_name].copy() - 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.""" - if not event_type: - self.logger.error('Bad event_type in dispatch_event.') - - event_name = event_type.value - - await self.dispatch_event_by_name(event_data, event_name) - - def _unpackage_event(self, enum_key: str, event): - event_key = event.WhichOneof(enum_key) - event_data = getattr(event, event_key) - if getattr(event_data, 'WhichOneof'): - # Object events are automatically unpackaged into their sub-event classes. - try: - return self._unpackage_event('object_event_type', event_data) - except ValueError: - pass - except TypeError: - pass - - return event_key, event_data - - async def _handle_event_stream(self): - self._conn_id = bytes(uuid.uuid4().hex, "utf-8") - try: - req = protocol.EventRequest(connection_id=self._conn_id) - async for evt in self._conn.grpc_interface.EventStream(req): - if not self.listening_for_events: - break - try: - unpackaged_event_key, unpackaged_event_data = self._unpackage_event('event_type', evt.event) - await self.dispatch_event_by_name(unpackaged_event_data, unpackaged_event_key) - except TypeError: - self.logger.warning('Unknown Event type') - 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, **kwargs): - """Receive a method call when the specified event occurs. - - .. testcode:: - - 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') - robot.conn.run_coroutine(robot.events.dispatch_event_by_name('my_event dispatched', event_name='my_event')) - - :param func: A method implemented in your code that will be called when the event is fired. - :param event_name: The name of the event that will result in func being called. - """ - 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(_EventCallback(func, on_connection_thread)) - - def subscribe(self, func: Callable, event_type: Events = None, **kwargs): - """Receive a method call when the specified event occurs. - - .. testcode:: - - 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: - - # If necessary, move Vector's Head and Lift to make it easy to see his face - robot.behavior.set_head_angle(degrees(45.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 ------") - - try: - if not evt.wait(timeout=5): - print("------ Vector never saw your face! ------") - except KeyboardInterrupt: - pass - - robot.events.unsubscribe(on_robot_observed_face, Events.robot_observed_face) - - :param func: A method implemented in your code that will be called when the event is fired. - :param event_type: The enum type of the event that will result in func being called. - """ - if not event_type: - self.logger.error('Bad event_type in subscribe.') - - event_name = event_type.value - - 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:: - - 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') - robot.conn.run_coroutine(robot.events.dispatch_event_by_name('my_event dispatched', event_name='my_event')) - - :param func: The method you no longer wish to be called when an event fires. - :param event_name: The name of the event for which you no longer want to receive a method call. - """ - if not event_name: - self.logger.error('Bad event_key in unsubscribe.') - - if event_name in self.subscribers.keys(): - event_subscribers = self.subscribers[event_name] - if func in event_subscribers: - event_subscribers.remove(func) - if not event_subscribers: - self.subscribers.pop(event_name, None) - else: - self.logger.error(f"The function '{func.__name__}' is not subscribed to '{event_name}'") - else: - self.logger.error(f"Cannot unsubscribe from event_type '{event_name}'. " - "It has no subscribers.") - - def unsubscribe(self, func: Callable, event_type: Events = None): - """Unregister a previously subscribed method from an event. - - .. testcode:: - - 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: - - # If necessary, move Vector's Head and Lift to make it easy to see his face - robot.behavior.set_head_angle(degrees(45.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 ------") - - try: - if not evt.wait(timeout=5): - print("------ Vector never saw your face! ------") - except KeyboardInterrupt: - pass - - robot.events.unsubscribe(on_robot_observed_face, Events.robot_observed_face) - - :param func: The enum type of the event you no longer wish to be called when an event fires. - :param event_type: The name of the event for which you no longer want to receive a method call. - """ - if not event_type: - self.logger.error('Bad event_type in unsubscribe.') - - event_name = event_type.value - - self.unsubscribe_by_name(func, event_name) diff --git a/anki_vector/exceptions.py b/anki_vector/exceptions.py deleted file mode 100644 index 6c25130..0000000 --- a/anki_vector/exceptions.py +++ /dev/null @@ -1,145 +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 -# -# https://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. - -""" -SDK-specific exception classes for Vector. -""" - -from grpc import RpcError, StatusCode - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['VectorCameraFeedDisabledException', - 'VectorConnectionException', - 'VectorControlException', - 'VectorControlTimeoutException', - 'VectorException', - 'VectorInvalidVersionException', - 'VectorNotFoundException', - 'VectorNotReadyException', - 'VectorTimeoutException', - 'VectorUnauthenticatedException', - 'VectorUnavailableException', - 'VectorUnimplementedException', - 'connection_error'] - - -class VectorException(Exception): - """Base class of all Vector SDK exceptions.""" - - -class VectorInvalidVersionException(VectorException): - """Your SDK version is not compatible with Vector's version.""" - - def __init__(self, version_request, version_response): - host = version_response.host_version - min_host = version_request.min_host_version - client = version_request.client_version - if min_host > host: - error_message = (f"{self.__class__.__doc__}\n\n" - f"Your Vector is an older version that is not supported by the SDK: min={min_host} > host={host}\n" - f"Use your app to make sure that Vector is on the internet, and able to download the latest update.") - else: - error_message = (f"{self.__class__.__doc__}\n\n" - f"Your SDK is an older version that is not supported by Vector: {host} > {client}\n" - f"Please install the latest SDK to continue.") - super().__init__(error_message) - - -class VectorControlException(VectorException): - """Unable to run a function which requires behavior control.""" - - def __init__(self, function): - msg = (f"Unable to run '{function}' because it requires behavior control.\n\n" - "Make sure to request control from Vector either by providing the 'enable_behavior_control' parameter to Robot, " - "or directly call 'request_control()' on your connection.") - super().__init__(msg) - - -class VectorConnectionException(VectorException): - def __init__(self, cause): - doc_str = self.__class__.__doc__ - if cause is not None: - self._status = cause.code() - self._details = cause.details() - msg = (f"{self._status}: {self._details}" - f"\n\n{doc_str if doc_str else 'Unknown error'}") - super().__init__(msg) - else: - super().__init__(doc_str) - - @property - def status(self): - return self._status - - @property - def details(self): - return self._details - - -class VectorUnauthenticatedException(VectorConnectionException): - """Failed to authenticate request.""" - - -class VectorUnavailableException(VectorConnectionException): - """Unable to reach Vector.""" - - -class VectorUnimplementedException(VectorConnectionException): - """Vector does not handle this message.""" - - -class VectorTimeoutException(VectorConnectionException): - """Message took too long to complete.""" - - -def connection_error(rpc_error: RpcError) -> VectorConnectionException: - """Translates grpc-specific errors to user-friendly :class:`VectorConnectionException`.""" - code = rpc_error.code() - if code is StatusCode.UNAUTHENTICATED: - return VectorUnauthenticatedException(rpc_error) - if code is StatusCode.UNAVAILABLE: - return VectorUnavailableException(rpc_error) - if code is StatusCode.UNIMPLEMENTED: - return VectorUnimplementedException(rpc_error) - if code is StatusCode.DEADLINE_EXCEEDED: - return VectorTimeoutException(rpc_error) - return VectorConnectionException(rpc_error) - - -class _VectorGenericException(VectorException): - def __init__(self, cause=None): - msg = (f"{self.__class__.__doc__}\n{cause if cause is not None else ''}") - super().__init__(msg) - - -class VectorNotFoundException(_VectorGenericException): - """Unable to establish a connection to Vector. - -Make sure you're on the same network, and Vector is connected to the internet. -""" - - -class VectorNotReadyException(_VectorGenericException): - """Vector tried to do something before it was ready.""" - - -class VectorControlTimeoutException(_VectorGenericException): - """Failed to get control of Vector. - -Please verify that Vector is connected to the internet, is on a flat surface, and is fully charged. -""" - - -class VectorCameraFeedDisabledException(VectorException): - """Failed to render video because camera feed was disabled.""" diff --git a/anki_vector/faces.py b/anki_vector/faces.py deleted file mode 100644 index c8d276f..0000000 --- a/anki_vector/faces.py +++ /dev/null @@ -1,388 +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 -# -# https://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. - -"""Face recognition and enrollment. - -Vector is capable of recognizing human faces, tracking their position and rotation -("pose") and assigning names to them via an enrollment process. - -The :class:`anki_vector.world.World` object keeps track of faces the robot currently -knows about, along with those that are currently visible to the camera. - -Each face is assigned a :class:`Face` object, which generates a number of -observable events whenever the face is observed, has its ID updated. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['Expression', 'Face', 'FaceComponent'] - -from enum import Enum -from typing import List - -from . import connection, util, objects, events -from .messaging import protocol - - -class Expression(Enum): - """Facial expressions that Vector can distinguish. - - Facial expression not recognized. - Call :func:`anki_vector.robot.Robot.vision.enable_face_detection(detect_faces=True)` to enable recognition. - """ - UNKNOWN = protocol.FacialExpression.Value("EXPRESSION_UNKNOWN") - #: Facial expression neutral - NEUTRAL = protocol.FacialExpression.Value("EXPRESSION_NEUTRAL") - #: Facial expression happiness - HAPPINESS = protocol.FacialExpression.Value("EXPRESSION_HAPPINESS") - #: Facial expression surprise - SURPRISE = protocol.FacialExpression.Value("EXPRESSION_SURPRISE") - #: Facial expression anger - ANGER = protocol.FacialExpression.Value("EXPRESSION_ANGER") - #: Facial expression sadness - SADNESS = protocol.FacialExpression.Value("EXPRESSION_SADNESS") - - -class Face(objects.ObservableObject): - """A single face that Vector has detected. - - May represent a face that has previously been enrolled, in which case - :attr:`name` will hold the name that it was enrolled with. - - Each Face instance has a :attr:`face_id` integer - This may change if - Vector later gets an improved view and makes a different prediction about - which face he is looking at. - """ - - def __init__(self, - robot, - pose: util.Pose, - image_rect: util.ImageRect, - face_id: int, - name: str, - expression: str, - expression_score: List[int], - left_eye: List[protocol.CladPoint], - right_eye: List[protocol.CladPoint], - nose: List[protocol.CladPoint], - mouth: List[protocol.CladPoint], - instantiation_timestamp: float, - **kw): - - super(Face, self).__init__(robot, **kw) - - self._face_id = face_id - self._updated_face_id = None - self._name = name - self._expression = expression - - # Individual expression values histogram, sums to 100 - # (Exception: all zero if expression=Unknown) - self._expression_score = expression_score - - # Face landmarks - self._left_eye = left_eye - self._right_eye = right_eye - self._nose = nose - self._mouth = mouth - - self._on_observed(pose, image_rect, instantiation_timestamp) - - self._robot.events.subscribe( - self._on_face_observed, - events.Events.robot_observed_face) - - self._robot.events.subscribe( - self._on_face_id_changed, - events.Events.robot_changed_observed_face_id) - - def __repr__(self): - return (f"<{self.__class__.__name__} Face id: {self.face_id} " - f"Updated face id: {self.updated_face_id} Name: {self.name} " - f"Expression: {protocol.FacialExpression.Name(self.expression)}>") - - def teardown(self): - """All faces will be torn down by the world when no longer needed.""" - self._robot.events.unsubscribe( - self._on_face_observed, - events.Events.robot_observed_face) - - self._robot.events.unsubscribe( - self._on_face_id_changed, - events.Events.robot_changed_observed_face_id) - - @property - def face_id(self) -> int: - """The internal ID assigned to the face. - - This value can only be assigned once as it is static in the engine. - - :getter: Returns the face ID - :setter: Sets the face ID - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - # Print the visible face ids - for face in robot.world.visible_faces: - print(f"Visible id: {face.face_id}") - """ - return self._face_id if self._updated_face_id is None else self._updated_face_id - - @face_id.setter - def face_id(self, face_id: str): - if self._face_id is not None: - raise ValueError(f"Cannot change face ID once set (from {self._face_id} to {face_id})") - self._face_id = face_id - - @property - def has_updated_face_id(self) -> bool: - """True if this face been updated / superseded by a face with a new ID. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - was_face_originally_unrecognized_but_is_now_recognized = face.has_updated_face_id - """ - return self._updated_face_id is not None - - @property - def updated_face_id(self) -> int: - """The ID for the face that superseded this one (if any, otherwise :meth:`face_id`) - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - print(f"updated_face_id: {face.updated_face_id}") - """ - if self._updated_face_id: - return self._updated_face_id - return self._face_id - - @property - def name(self) -> str: - """The name Vector has associated with the face. - - This string will be empty if the face is not recognized or enrolled. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - print(f"Face name: {face.name}") - """ - return self._name - - @property - def expression(self) -> str: - """The facial expression Vector has recognized on the face. - - Will be :attr:`Expression.UNKNOWN` by default if you haven't called - :meth:`anki_vector.robot.Robot.vision.enable_face_detection(detect_faces=True, estimate_emotion=True)` to enable - the facial expression estimation. Otherwise it will be equal to one of: - :attr:`Expression.NEUTRAL`, :attr:`Expression.HAPPINESS`, - :attr:`Expression.SURPRISE`, :attr:`Expression.ANGER`, - or :attr:`Expression.SADNESS`. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - print(f"expression: {face.expression}") - """ - return self._expression - - @property - def expression_score(self) -> List[int]: - """The score/confidence that :attr:`expression` was correct. - - Will be 0 if expression is :attr:`Expression.UNKNOWN` (e.g. if - :meth:`anki_vector.robot.Robot.vision.enable_face_detection(detect_faces=True, estimate_emotion=True)` wasn't - called yet). The maximum possible score is 100. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - print(f"expression_score: {face.expression_score}") - """ - return self._expression_score - - @property - def left_eye(self) -> List[protocol.CladPoint]: - """sequence of tuples of float (x,y): points representing the outline of the left eye. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - print(f"left_eye: {face.left_eye}") - """ - return self._left_eye - - @property - def right_eye(self) -> List[protocol.CladPoint]: - """sequence of tuples of float (x,y): points representing the outline of the right eye. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - print(f"right_eye: {face.right_eye}") - """ - return self._right_eye - - @property - def nose(self) -> List[protocol.CladPoint]: - """sequence of tuples of float (x,y): points representing the outline of the nose. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - print(f"nose: {face.nose}") - """ - return self._nose - - @property - def mouth(self) -> List[protocol.CladPoint]: - """sequence of tuples of float (x,y): points representing the outline of the mouth. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - print(f"mouth: {face.mouth}") - """ - return self._mouth - - #### Private Event Handlers #### - - def _on_face_observed(self, _, msg): - """Unpacks the face observed stream data from Vector into a Face instance.""" - if self._face_id == msg.face_id: - - pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z, - q0=msg.pose.q0, q1=msg.pose.q1, - q2=msg.pose.q2, q3=msg.pose.q3, - origin_id=msg.pose.origin_id) - image_rect = util.ImageRect(msg.img_rect.x_top_left, - msg.img_rect.y_top_left, - msg.img_rect.width, - msg.img_rect.height) - - self._name = msg.name - - self._expression = msg.expression - self._expression_score = msg.expression_values - self._left_eye = msg.left_eye - self._right_eye = msg.right_eye - self._nose = msg.nose - self._mouth = msg.mouth - self._on_observed(pose, image_rect, msg.timestamp) - - def _on_face_id_changed(self, _, msg): - """Updates the face id when a tracked face (negative ID) is recognized and - receives a positive ID or when face records get merged""" - if self._face_id == msg.old_id: - self._updated_face_id = msg.new_id - - -class FaceComponent(util.Component): - """Manage the state of the faces on the robot.""" - - @connection.on_connection_thread(requires_control=False) - async def request_enrolled_names(self) -> protocol.RequestEnrolledNamesRequest: - """Asks the robot for the list of names attached to faces that it can identify. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - name_data_list = robot.faces.request_enrolled_names() - print(f"{name_data_list}") - """ - req = protocol.RequestEnrolledNamesRequest() - return await self.grpc_interface.RequestEnrolledNames(req) - - @connection.on_connection_thread(requires_control=False) - async def update_enrolled_face_by_id(self, face_id: int, old_name: str, new_name: str): - """Update the name enrolled for a given face. - - :param face_id: The ID of the face to rename. - :param old_name: The old name of the face (must be correct, otherwise message is ignored). - :param new_name: The new name for the face. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.faces.update_enrolled_face_by_id(1, 'Hanns', 'Boris') - """ - req = protocol.UpdateEnrolledFaceByIDRequest(face_id=face_id, - old_name=old_name, new_name=new_name) - return await self.grpc_interface.UpdateEnrolledFaceByID(req) - - @connection.on_connection_thread(requires_control=False) - async def erase_enrolled_face_by_id(self, face_id: int): - """Erase the enrollment (name) record for the face with this ID. - - :param face_id: The ID of the face to erase. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.faces.erase_enrolled_face_by_id(1) - """ - req = protocol.EraseEnrolledFaceByIDRequest(face_id=face_id) - return await self.grpc_interface.EraseEnrolledFaceByID(req) - - @connection.on_connection_thread(requires_control=False) - async def erase_all_enrolled_faces(self): - """Erase the enrollment (name) records for all faces. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.faces.erase_all_enrolled_faces() - """ - req = protocol.EraseAllEnrolledFacesRequest() - return await self.grpc_interface.EraseAllEnrolledFaces(req) diff --git a/anki_vector/lights.py b/anki_vector/lights.py deleted file mode 100755 index 8ea84e6..0000000 --- a/anki_vector/lights.py +++ /dev/null @@ -1,210 +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 -# -# https://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. - -"""Helper routines for dealing with Vector's lights and colors.""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['MAX_COLOR_PROFILE', 'WHITE_BALANCED_CUBE_PROFILE', - 'blue_light', 'cyan_light', 'green_light', 'magenta_light', 'off_light', - 'red_light', 'white_light', 'yellow_light', - 'Color', 'ColorProfile', 'Light', 'package_request_params'] - -from .color import Color, green, red, blue, cyan, magenta, yellow, white, off - - -class ColorProfile: - """Applies transforms to make Vector's lights and colors appear as - intended, by limiting maximum channel intensity. - - :param red_multiplier: Scaling value for the brightness of red Lights - :param green_multiplier: Scaling value for the brightness of green Lights - :param blue_multiplier: Scaling value for the brightness of blue Lights - """ - - def __init__(self, red_multiplier: float, green_multiplier: float, blue_multiplier: float): - self._red_multiplier = red_multiplier - self._green_multiplier = green_multiplier - self._blue_multiplier = blue_multiplier - - # TODO Needs docs, param types, sample code - def augment_color(self, original_color): - rgb = [ - (original_color.int_color >> 24) & 0xff, - (original_color.int_color >> 16) & 0xff, - (original_color.int_color >> 8) & 0xff - ] - - rgb[0] = int(self._red_multiplier * rgb[0]) - rgb[1] = int(self._green_multiplier * rgb[1]) - rgb[2] = int(self._blue_multiplier * rgb[2]) - - result_int_code = (rgb[0] << 24) | (rgb[1] << 16) | (rgb[2] << 8) | 0xff - return Color(result_int_code) - - # TODO Needs example code, more descriptive docs - @property - def red_multiplier(self): - """float: The multiplier used on the red channel.""" - return self._red_multiplier - - # TODO Needs example code, more descriptive docs - @property - def green_multiplier(self): - """float: The multiplier used on the red channel.""" - return self._green_multiplier - - # TODO Needs example code, more descriptive docs - @property - def blue_multiplier(self): - """float: The multiplier used on the red channel.""" - return self._blue_multiplier - - -#: :class:`ColorProfile`: Color profile to get the maximum possible brightness out of each LED. -MAX_COLOR_PROFILE = ColorProfile(red_multiplier=1.0, - green_multiplier=1.0, - blue_multiplier=1.0) - -#: :class:`ColorProfile`: Color profile balanced so that a max color value more closely resembles pure white. -# TODO: Balance this more carefully once robots with proper color pipe -# hardware becomes available -WHITE_BALANCED_CUBE_PROFILE = ColorProfile(red_multiplier=1.0, - green_multiplier=0.95, - blue_multiplier=0.7) - - -class Light: - """Lights are used with Vector's Cube. - - Lights may either be "on" or "off", though in practice any colors may be - assigned to either state (including no color/light). - """ - - def __init__(self, - on_color: Color = off, - off_color: Color = off, - on_period_ms: int = 250, - off_period_ms: int = 0, - transition_on_period_ms: int = 0, - transition_off_period_ms: int = 0): - self._on_color = on_color - self._off_color = off_color - self._on_period_ms = on_period_ms - self._off_period_ms = off_period_ms - self._transition_on_period_ms = transition_on_period_ms - self._transition_off_period_ms = transition_off_period_ms - - @property - def on_color(self) -> Color: - """The color shown when the light is on.""" - return self._on_color - - @on_color.setter - def on_color(self, color): - if not isinstance(color, Color): - raise TypeError("Must specify a Color") - self._on_color = color - - @property - def off_color(self) -> Color: - """The color shown when the light is off.""" - return self._off_color - - @off_color.setter - def off_color(self, color): - if not isinstance(color, Color): - raise TypeError("Must specify a Color") - self._off_color = color - - @property - def on_period_ms(self) -> int: - """The number of milliseconds the light should be "on" for for each cycle.""" - return self._on_period_ms - - @on_period_ms.setter - def on_period_ms(self, ms): - if not 0 < ms < 2**32: - raise ValueError("Invalid value") - self._on_period_ms = ms - - @property - def off_period_ms(self) -> int: - """The number of milliseconds the light should be "off" for for each cycle.""" - return self._off_period_ms - - @off_period_ms.setter - def off_period_ms(self, ms): - if not 0 < ms < 2**32: - raise ValueError("Invalid value") - self._off_period_ms = ms - - @property - def transition_on_period_ms(self) -> int: - """The number of milliseconds to take to transition the light to the on color.""" - return self._transition_on_period_ms - - @transition_on_period_ms.setter - def transition_on_period_ms(self, ms): - if not 0 < ms < 2**32: - raise ValueError("Invalid value") - self._transition_on_period_ms = ms - - @property - def transition_off_period_ms(self) -> int: - """The number of milliseconds to take to transition the light to the off color.""" - return self._transition_off_period_ms - - @transition_off_period_ms.setter - def transition_off_period_ms(self, ms): - if not 0 < ms < 2**32: - raise ValueError("Invalid value") - self._transition_off_period_ms = ms - - -# TODO needs docs, param types. Should this be private? Maybe a more descriptive name? -def package_request_params(lights, color_profile): - merged_params = {} - for light in lights: - for attr_name in vars(light): - attr_name = attr_name[1:] - attr_val = getattr(light, attr_name) - if isinstance(attr_val, Color): - attr_val = color_profile.augment_color(attr_val).int_color - merged_params.setdefault(attr_name, []).append(attr_val) - return merged_params - - -#: :class:`Light`: A steady green colored LED light. -green_light = Light(on_color=green) - -#: :class:`Light`: A steady red colored LED light. -red_light = Light(on_color=red) - -#: :class:`Light`: A steady blue colored LED light. -blue_light = Light(on_color=blue) - -#: :class:`Light`: A steady cyan colored LED light. -cyan_light = Light(on_color=cyan) - -#: :class:`Light`: A steady magenta colored LED light. -magenta_light = Light(on_color=magenta) - -#: :class:`Light`: A steady yellow colored LED light. -yellow_light = Light(on_color=yellow) - -#: :class:`Light`: A steady white colored LED light. -white_light = Light(on_color=white) - -#: :class:`Light`: A steady off (non-illuminated LED light). -off_light = Light(on_color=off) diff --git a/anki_vector/messaging/__init__.py b/anki_vector/messaging/__init__.py deleted file mode 100644 index a08083b..0000000 --- a/anki_vector/messaging/__init__.py +++ /dev/null @@ -1,22 +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. - -""" -Protobuf and gRPC messages exposed to the Vector Python SDK -""" - -from . import protocol -from . import client - -__all__ = ['protocol', 'client'] diff --git a/anki_vector/messaging/alexa.proto b/anki_vector/messaging/alexa.proto deleted file mode 100644 index d4397d7..0000000 --- a/anki_vector/messaging/alexa.proto +++ /dev/null @@ -1,21 +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. - -// Alexa messages - -syntax = "proto3"; - -package Anki.Vector.external_interface; - - diff --git a/anki_vector/messaging/alexa_pb2.py b/anki_vector/messaging/alexa_pb2.py deleted file mode 100644 index bcb059d..0000000 --- a/anki_vector/messaging/alexa_pb2.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/alexa.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/alexa.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - serialized_pb=_b('\n!anki_vector/messaging/alexa.proto\x12\x1e\x41nki.Vector.external_interfaceb\x06proto3') -) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/alexa_pb2_grpc.py b/anki_vector/messaging/alexa_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/alexa_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/behavior.proto b/anki_vector/messaging/behavior.proto deleted file mode 100644 index 2d1faf7..0000000 --- a/anki_vector/messaging/behavior.proto +++ /dev/null @@ -1,64 +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. - -// Behavior related messages - -syntax = "proto3"; - -package Anki.Vector.external_interface; - -import "anki_vector/messaging/messages.proto"; - -message ControlRelease {} - -// Request control of the behavior system at a given priority -message ControlRequest { - enum Priority { - UNKNOWN = 0; - // Request control above mandatory physical reactions. Not recommended. - OVERRIDE_ALL = 10; - // Highest priority level. Directly under mandatory physical reactions. - TOP_PRIORITY_AI = 20; - } - Priority priority = 1; -} - -// Messages that can be sent to the behavior stream. Explicitly -// requesting or releasing control. -message BehaviorControlRequest { - oneof request_type { - ControlRelease control_release = 1; - ControlRequest control_request = 2; - } -} - -// The SDK user is now free to run any actions and behaviors they like. -// Until a ControlLostResponse is received, they are directly in control -// of Vector's behavior system. -message ControlGrantedResponse {} - -// This informs the user that they lost control of the behavior system. -// All direct actions will be unavailable via the sdk until control is regained. -// Regaining control can be either through a call to ControlRequest, or -// can be as a result of conditions passed to the original ControlRequest. -message ControlLostResponse {} - -// Responses from the behavior stream. -message BehaviorControlResponse { - oneof response_type { - ControlGrantedResponse control_granted_response = 1; - ControlLostResponse control_lost_event = 2; - KeepAlivePing keep_alive = 3; - } -} \ No newline at end of file diff --git a/anki_vector/messaging/behavior_pb2.py b/anki_vector/messaging/behavior_pb2.py deleted file mode 100644 index 0e708b0..0000000 --- a/anki_vector/messaging/behavior_pb2.py +++ /dev/null @@ -1,321 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/behavior.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() - - -from anki_vector.messaging import messages_pb2 as anki__vector_dot_messaging_dot_messages__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='anki_vector/messaging/behavior.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - serialized_pb=_b('\n$anki_vector/messaging/behavior.proto\x12\x1e\x41nki.Vector.external_interface\x1a$anki_vector/messaging/messages.proto\"\x10\n\x0e\x43ontrolRelease\"\x9b\x01\n\x0e\x43ontrolRequest\x12I\n\x08priority\x18\x01 \x01(\x0e\x32\x37.Anki.Vector.external_interface.ControlRequest.Priority\">\n\x08Priority\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x10\n\x0cOVERRIDE_ALL\x10\n\x12\x13\n\x0fTOP_PRIORITY_AI\x10\x14\"\xbe\x01\n\x16\x42\x65haviorControlRequest\x12I\n\x0f\x63ontrol_release\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ControlReleaseH\x00\x12I\n\x0f\x63ontrol_request\x18\x02 \x01(\x0b\x32..Anki.Vector.external_interface.ControlRequestH\x00\x42\x0e\n\x0crequest_type\"\x18\n\x16\x43ontrolGrantedResponse\"\x15\n\x13\x43ontrolLostResponse\"\x9e\x02\n\x17\x42\x65haviorControlResponse\x12Z\n\x18\x63ontrol_granted_response\x18\x01 \x01(\x0b\x32\x36.Anki.Vector.external_interface.ControlGrantedResponseH\x00\x12Q\n\x12\x63ontrol_lost_event\x18\x02 \x01(\x0b\x32\x33.Anki.Vector.external_interface.ControlLostResponseH\x00\x12\x43\n\nkeep_alive\x18\x03 \x01(\x0b\x32-.Anki.Vector.external_interface.KeepAlivePingH\x00\x42\x0f\n\rresponse_typeb\x06proto3') - , - dependencies=[anki__vector_dot_messaging_dot_messages__pb2.DESCRIPTOR,]) - - - -_CONTROLREQUEST_PRIORITY = _descriptor.EnumDescriptor( - name='Priority', - full_name='Anki.Vector.external_interface.ControlRequest.Priority', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OVERRIDE_ALL', index=1, number=10, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TOP_PRIORITY_AI', index=2, number=20, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=222, - serialized_end=284, -) -_sym_db.RegisterEnumDescriptor(_CONTROLREQUEST_PRIORITY) - - -_CONTROLRELEASE = _descriptor.Descriptor( - name='ControlRelease', - full_name='Anki.Vector.external_interface.ControlRelease', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=110, - serialized_end=126, -) - - -_CONTROLREQUEST = _descriptor.Descriptor( - name='ControlRequest', - full_name='Anki.Vector.external_interface.ControlRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='priority', full_name='Anki.Vector.external_interface.ControlRequest.priority', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _CONTROLREQUEST_PRIORITY, - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=129, - serialized_end=284, -) - - -_BEHAVIORCONTROLREQUEST = _descriptor.Descriptor( - name='BehaviorControlRequest', - full_name='Anki.Vector.external_interface.BehaviorControlRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='control_release', full_name='Anki.Vector.external_interface.BehaviorControlRequest.control_release', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='control_request', full_name='Anki.Vector.external_interface.BehaviorControlRequest.control_request', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - _descriptor.OneofDescriptor( - name='request_type', full_name='Anki.Vector.external_interface.BehaviorControlRequest.request_type', - index=0, containing_type=None, fields=[]), - ], - serialized_start=287, - serialized_end=477, -) - - -_CONTROLGRANTEDRESPONSE = _descriptor.Descriptor( - name='ControlGrantedResponse', - full_name='Anki.Vector.external_interface.ControlGrantedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=479, - serialized_end=503, -) - - -_CONTROLLOSTRESPONSE = _descriptor.Descriptor( - name='ControlLostResponse', - full_name='Anki.Vector.external_interface.ControlLostResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=505, - serialized_end=526, -) - - -_BEHAVIORCONTROLRESPONSE = _descriptor.Descriptor( - name='BehaviorControlResponse', - full_name='Anki.Vector.external_interface.BehaviorControlResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='control_granted_response', full_name='Anki.Vector.external_interface.BehaviorControlResponse.control_granted_response', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='control_lost_event', full_name='Anki.Vector.external_interface.BehaviorControlResponse.control_lost_event', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keep_alive', full_name='Anki.Vector.external_interface.BehaviorControlResponse.keep_alive', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - _descriptor.OneofDescriptor( - name='response_type', full_name='Anki.Vector.external_interface.BehaviorControlResponse.response_type', - index=0, containing_type=None, fields=[]), - ], - serialized_start=529, - serialized_end=815, -) - -_CONTROLREQUEST.fields_by_name['priority'].enum_type = _CONTROLREQUEST_PRIORITY -_CONTROLREQUEST_PRIORITY.containing_type = _CONTROLREQUEST -_BEHAVIORCONTROLREQUEST.fields_by_name['control_release'].message_type = _CONTROLRELEASE -_BEHAVIORCONTROLREQUEST.fields_by_name['control_request'].message_type = _CONTROLREQUEST -_BEHAVIORCONTROLREQUEST.oneofs_by_name['request_type'].fields.append( - _BEHAVIORCONTROLREQUEST.fields_by_name['control_release']) -_BEHAVIORCONTROLREQUEST.fields_by_name['control_release'].containing_oneof = _BEHAVIORCONTROLREQUEST.oneofs_by_name['request_type'] -_BEHAVIORCONTROLREQUEST.oneofs_by_name['request_type'].fields.append( - _BEHAVIORCONTROLREQUEST.fields_by_name['control_request']) -_BEHAVIORCONTROLREQUEST.fields_by_name['control_request'].containing_oneof = _BEHAVIORCONTROLREQUEST.oneofs_by_name['request_type'] -_BEHAVIORCONTROLRESPONSE.fields_by_name['control_granted_response'].message_type = _CONTROLGRANTEDRESPONSE -_BEHAVIORCONTROLRESPONSE.fields_by_name['control_lost_event'].message_type = _CONTROLLOSTRESPONSE -_BEHAVIORCONTROLRESPONSE.fields_by_name['keep_alive'].message_type = anki__vector_dot_messaging_dot_messages__pb2._KEEPALIVEPING -_BEHAVIORCONTROLRESPONSE.oneofs_by_name['response_type'].fields.append( - _BEHAVIORCONTROLRESPONSE.fields_by_name['control_granted_response']) -_BEHAVIORCONTROLRESPONSE.fields_by_name['control_granted_response'].containing_oneof = _BEHAVIORCONTROLRESPONSE.oneofs_by_name['response_type'] -_BEHAVIORCONTROLRESPONSE.oneofs_by_name['response_type'].fields.append( - _BEHAVIORCONTROLRESPONSE.fields_by_name['control_lost_event']) -_BEHAVIORCONTROLRESPONSE.fields_by_name['control_lost_event'].containing_oneof = _BEHAVIORCONTROLRESPONSE.oneofs_by_name['response_type'] -_BEHAVIORCONTROLRESPONSE.oneofs_by_name['response_type'].fields.append( - _BEHAVIORCONTROLRESPONSE.fields_by_name['keep_alive']) -_BEHAVIORCONTROLRESPONSE.fields_by_name['keep_alive'].containing_oneof = _BEHAVIORCONTROLRESPONSE.oneofs_by_name['response_type'] -DESCRIPTOR.message_types_by_name['ControlRelease'] = _CONTROLRELEASE -DESCRIPTOR.message_types_by_name['ControlRequest'] = _CONTROLREQUEST -DESCRIPTOR.message_types_by_name['BehaviorControlRequest'] = _BEHAVIORCONTROLREQUEST -DESCRIPTOR.message_types_by_name['ControlGrantedResponse'] = _CONTROLGRANTEDRESPONSE -DESCRIPTOR.message_types_by_name['ControlLostResponse'] = _CONTROLLOSTRESPONSE -DESCRIPTOR.message_types_by_name['BehaviorControlResponse'] = _BEHAVIORCONTROLRESPONSE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -ControlRelease = _reflection.GeneratedProtocolMessageType('ControlRelease', (_message.Message,), dict( - DESCRIPTOR = _CONTROLRELEASE, - __module__ = 'anki_vector.messaging.behavior_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ControlRelease) - )) -_sym_db.RegisterMessage(ControlRelease) - -ControlRequest = _reflection.GeneratedProtocolMessageType('ControlRequest', (_message.Message,), dict( - DESCRIPTOR = _CONTROLREQUEST, - __module__ = 'anki_vector.messaging.behavior_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ControlRequest) - )) -_sym_db.RegisterMessage(ControlRequest) - -BehaviorControlRequest = _reflection.GeneratedProtocolMessageType('BehaviorControlRequest', (_message.Message,), dict( - DESCRIPTOR = _BEHAVIORCONTROLREQUEST, - __module__ = 'anki_vector.messaging.behavior_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.BehaviorControlRequest) - )) -_sym_db.RegisterMessage(BehaviorControlRequest) - -ControlGrantedResponse = _reflection.GeneratedProtocolMessageType('ControlGrantedResponse', (_message.Message,), dict( - DESCRIPTOR = _CONTROLGRANTEDRESPONSE, - __module__ = 'anki_vector.messaging.behavior_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ControlGrantedResponse) - )) -_sym_db.RegisterMessage(ControlGrantedResponse) - -ControlLostResponse = _reflection.GeneratedProtocolMessageType('ControlLostResponse', (_message.Message,), dict( - DESCRIPTOR = _CONTROLLOSTRESPONSE, - __module__ = 'anki_vector.messaging.behavior_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ControlLostResponse) - )) -_sym_db.RegisterMessage(ControlLostResponse) - -BehaviorControlResponse = _reflection.GeneratedProtocolMessageType('BehaviorControlResponse', (_message.Message,), dict( - DESCRIPTOR = _BEHAVIORCONTROLRESPONSE, - __module__ = 'anki_vector.messaging.behavior_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.BehaviorControlResponse) - )) -_sym_db.RegisterMessage(BehaviorControlResponse) - - -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/behavior_pb2_grpc.py b/anki_vector/messaging/behavior_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/behavior_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/client.py b/anki_vector/messaging/client.py deleted file mode 100644 index dc39d52..0000000 --- a/anki_vector/messaging/client.py +++ /dev/null @@ -1,33 +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. - -# pylint: skip-file - -""" -Protobuf messages exposed to the Vector Python SDK. -""" -import sys -import inspect - -from .alexa_pb2_grpc import * -from .behavior_pb2_grpc import * -from .cube_pb2_grpc import * -from .messages_pb2_grpc import * -from .nav_map_pb2_grpc import * -from .response_status_pb2_grpc import * -from .settings_pb2_grpc import * -from .shared_pb2_grpc import * -from .external_interface_pb2_grpc import * - -__all__ = [obj.__name__ for _, obj in inspect.getmembers(sys.modules[__name__]) if inspect.isclass(obj)] diff --git a/anki_vector/messaging/cube.proto b/anki_vector/messaging/cube.proto deleted file mode 100644 index 7fced85..0000000 --- a/anki_vector/messaging/cube.proto +++ /dev/null @@ -1,366 +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. - -// External interface for robot <-> app and robot <-> sdk communication - -syntax = "proto3"; - -package Anki.Vector.external_interface; - -import "anki_vector/messaging/messages.proto"; -import "anki_vector/messaging/response_status.proto"; - -// Attempt to connect to a cube. If a cube is currently connected, -// this will do nothing. -message ConnectCubeRequest { -} - -message ConnectCubeResponse { - ResponseStatus status = 1; - bool success = 2; - uint32 object_id = 3; - string factory_id = 4; -} - -message CubesAvailableRequest { -} - -message CubesAvailableResponse { - ResponseStatus status = 1; - repeated string factory_ids = 2; -} - -// Requests a disconnection from the currently connected cube. -message DisconnectCubeRequest { -} - -message DisconnectCubeResponse { - ResponseStatus status = 1; -} - -// Plays the default cube connection animation on the currently -// connected cube's lights. This is intended for app level user -// surfacing of cube connectivity, not for sdk cube light control. -message FlashCubeLightsRequest { -} - -message FlashCubeLightsResponse { - ResponseStatus status = 1; -} - -// Forget the robot's preferred cube. This will cause the robot to -// connect to the cube with the highest RSSI (signal strength) next -// time a connection is requested. Saves this preference to disk. -// The next cube that the robot connects to will become its -// preferred cube. -message ForgetPreferredCubeRequest { -} - -message ForgetPreferredCubeResponse { - ResponseStatus status = 1; -} - -// Set the robot's preferred cube and save it to disk. The robot -// will always attempt to connect to this cube if it is available. -// This is only used in simulation for now. -message SetPreferredCubeRequest { - string factory_id = 1; -} - -message SetPreferredCubeResponse { - ResponseStatus status = 1; -} - -// Sets each LED on victor's cube. Two states are specified -// designated 'on' and 'off', each with a color, duration, -// and state transition time -message SetCubeLightsRequest { - // This is a uint_8 internally. - enum MakeRelativeMode - { - UNKNOWN = 0; - OFF = 1; - BY_CORNER = 2; - BY_SIDE = 3; - } - - uint32 object_id = 1; - repeated uint32 on_color = 2; - repeated uint32 off_color = 3; - repeated uint32 on_period_ms = 4; - repeated uint32 off_period_ms = 5; - repeated uint32 transition_on_period_ms = 6; - repeated uint32 transition_off_period_ms = 7; - repeated int32 offset = 8; - float relative_to_x = 9; - float relative_to_y = 10; - bool rotate = 11; - MakeRelativeMode make_relative = 12; -} -message SetCubeLightsResponse { - ResponseStatus status = 1; -} - -enum ObjectType -{ - INVALID_OBJECT = 0; - UNKNOWN_OBJECT = 1; - - ////////////////////////////////////////////////////////////////////////// - // Light Cube (a.k.a. Active Block) - // - BLOCK_LIGHTCUBE1 = 2; - - FIRST_CUSTOM_OBJECT_TYPE = 15; - - OBJECT_TYPE_COUNT = 38; -} - -enum CustomType -{ - option allow_alias = true; - - INVALID_CUSTOM_TYPE = 0; - - CUSTOM_TYPE_00 = 1; - CUSTOM_TYPE_01 = 2; - CUSTOM_TYPE_02 = 3; - CUSTOM_TYPE_03 = 4; - CUSTOM_TYPE_04 = 5; - CUSTOM_TYPE_05 = 6; - CUSTOM_TYPE_06 = 7; - CUSTOM_TYPE_07 = 8; - CUSTOM_TYPE_08 = 9; - CUSTOM_TYPE_09 = 10; - CUSTOM_TYPE_10 = 11; - CUSTOM_TYPE_11 = 12; - CUSTOM_TYPE_12 = 13; - CUSTOM_TYPE_13 = 14; - CUSTOM_TYPE_14 = 15; - CUSTOM_TYPE_15 = 16; - CUSTOM_TYPE_16 = 17; - CUSTOM_TYPE_17 = 18; - CUSTOM_TYPE_18 = 19; - CUSTOM_TYPE_19 = 20; - - CUSTOM_TYPE_COUNT = 20; -} - -enum ObjectFamily { - option allow_alias = true; - - INVALID_FAMILY = 0; - UNKNOWN_FAMILY = 1; - BLOCK = 2; - LIGHT_CUBE = 3; - CHARGER = 4; - CUSTOM_OBJECT = 7; - - OBJECT_FAMILY_COUNT = 7; -} - -message ObjectAvailable { - string factory_id = 1; -} - -// Indicates that a cube has connected or disconnected to the robot. -// This message will be sent for any connects or disconnects regardless -// of whether it originated from us or underlying robot behavior. -message ObjectConnectionState { - uint32 object_id = 1; - string factory_id = 2; - ObjectType object_type = 3; - bool connected = 4; -} - -message ObjectMoved { - uint32 timestamp = 1; - uint32 object_id = 2; -} - -message ObjectStoppedMoving { - uint32 timestamp = 1; - uint32 object_id = 2; -} - -enum UpAxis { - INVALID_AXIS = 0; - X_NEGATIVE = 1; - X_POSITIVE = 2; - Y_NEGATIVE = 3; - Y_POSITIVE = 4; - Z_NEGATIVE = 5; - Z_POSITIVE = 6; - NUM_AXES = 7; -} - -message ObjectUpAxisChanged { - uint32 timestamp = 1; - uint32 object_id = 2; - UpAxis up_axis = 3; -} - -message ObjectTapped { - uint32 timestamp = 1; - uint32 object_id = 2; -} - -// RobotObservedObject for signaling that an object -// with specified ID/Type/Family was seen at a particular location in the image -// and the world -message RobotObservedObject { - uint32 timestamp = 1; - ObjectFamily object_family = 2; - ObjectType object_type = 3; - int32 object_id = 4; // signed to match U2G::PickAndPlaceObject which has the option to have objectID<0 - CladRect img_rect = 5; // position in image coords - PoseStruct pose = 6; - float top_face_orientation_rad = 7; // absolute orienation of top face, iff isActive==true - uint32 is_active = 8; -} - -// Indicates that a the connection subscribed through ConnectCube has -// been lost. -message CubeConnectionLost { -} - -// Constants associated with the audio feed -enum ObjectConstants { - OBJECT_CONSTANTS_NULL = 0; // error value - FIXED_CUSTOM_WALL_THICKNESS_MM = 10; // The depth of custom walls used in the engine -} - -enum CustomObjectMarker { - option allow_alias = true; - - CUSTOM_MARKER_UNKNOWN = 0; - - CUSTOM_MARKER_CIRCLES_2 = 1; - CUSTOM_MARKER_CIRCLES_3 = 2; - CUSTOM_MARKER_CIRCLES_4 = 3; - CUSTOM_MARKER_CIRCLES_5 = 4; - - CUSTOM_MARKER_DIAMONDS_2 = 5; - CUSTOM_MARKER_DIAMONDS_3 = 6; - CUSTOM_MARKER_DIAMONDS_4 = 7; - CUSTOM_MARKER_DIAMONDS_5 = 8; - - CUSTOM_MARKER_HEXAGONS_2 = 9; - CUSTOM_MARKER_HEXAGONS_3 = 10; - CUSTOM_MARKER_HEXAGONS_4 = 11; - CUSTOM_MARKER_HEXAGONS_5 = 12; - - CUSTOM_MARKER_TRIANGLES_2 = 13; - CUSTOM_MARKER_TRIANGLES_3 = 14; - CUSTOM_MARKER_TRIANGLES_4 = 15; - CUSTOM_MARKER_TRIANGLES_5 = 16; - - CUSTOM_MARKER_COUNT = 16; -} - -enum CustomObjectDeletionMode { - DELETION_MASK_UNKNOWN = 0; - DELETION_MASK_FIXED_CUSTOM_OBJECTS = 1; - DELETION_MASK_CUSTOM_MARKER_OBJECTS = 2; - DELETION_MASK_ARCHETYPES = 3; -} - -// Deletes all custom objects matching a given deletion mode. -message DeleteCustomObjectsRequest { - CustomObjectDeletionMode mode = 1; -} - -message DeleteCustomObjectsResponse { - ResponseStatus status = 1; -} - -// Creates a CustomObject at the specific pose with no markers associated with it -// Since this object has no markers with which to be observed, it will remain in the -// specified pose as an obstacle forever (or until deleted with a deletion message above) -message CreateFixedCustomObjectRequest { - PoseStruct pose = 1; - float x_size_mm = 2; - float y_size_mm = 3; - float z_size_mm = 4; -} - -message CreateFixedCustomObjectResponse { - ResponseStatus status = 1; - uint32 object_id = 2; -} - -// Defines a custom object of the given size with the given markers centered on each side -// If isUnique=true, there is guaranteed to be no more than one object of this type present in the world -message CustomBoxDefinition { - CustomObjectMarker marker_front = 1; - CustomObjectMarker marker_back = 2; - CustomObjectMarker marker_top = 3; - CustomObjectMarker marker_bottom = 4; - CustomObjectMarker marker_left = 5; - CustomObjectMarker marker_right = 6; - float x_size_mm = 7; - float y_size_mm = 8; - float z_size_mm = 9; - float marker_width_mm = 10; - float marker_height_mm = 11; -} - -// Defines a custom cube of the given size. The cube will have the same marker centered on all faces. -// If isUnique=true, there is guaranteed to be no more than one cube of this type present in the world at a time. -message CustomCubeDefinition { - CustomObjectMarker marker = 1; - float size_mm = 2; - float marker_width_mm = 3; - float marker_height_mm = 4; -} - -// Defines a custom wall of the given height and width, with the same marker centered on both sides (front and back). -// The wall's thickness is assumed to be 1cm (and thus there are no markers on its left, right, top, or bottom) -// If isUnique=true, there is guaranteed to be no more than one wall of this type present in the world at a time. -message CustomWallDefinition { - CustomObjectMarker marker = 1; - float width_mm = 2; - float height_mm = 3; - float marker_width_mm = 4; - float marker_height_mm = 5; -} - -message DefineCustomObjectRequest { - CustomType custom_type = 1; - bool is_unique = 2; - - oneof custom_object_definition { - CustomBoxDefinition custom_box = 3; - CustomCubeDefinition custom_cube = 4; - CustomWallDefinition custom_wall = 5; - } -} - -message DefineCustomObjectResponse { - ResponseStatus status = 1; - bool success = 2; -} - -message ObjectEvent { - oneof object_event_type { - ObjectAvailable object_available = 1; - ObjectConnectionState object_connection_state = 2; - ObjectMoved object_moved = 3; - ObjectStoppedMoving object_stopped_moving = 4; - ObjectUpAxisChanged object_up_axis_changed = 5; - ObjectTapped object_tapped = 6; - RobotObservedObject robot_observed_object = 7; - CubeConnectionLost cube_connection_lost = 8; - } -} diff --git a/anki_vector/messaging/cube_pb2.py b/anki_vector/messaging/cube_pb2.py deleted file mode 100644 index c50f9ac..0000000 --- a/anki_vector/messaging/cube_pb2.py +++ /dev/null @@ -1,2239 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/cube.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -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() - - -from anki_vector.messaging import messages_pb2 as anki__vector_dot_messaging_dot_messages__pb2 -from anki_vector.messaging import response_status_pb2 as anki__vector_dot_messaging_dot_response__status__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='anki_vector/messaging/cube.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - serialized_pb=_b('\n anki_vector/messaging/cube.proto\x12\x1e\x41nki.Vector.external_interface\x1a$anki_vector/messaging/messages.proto\x1a+anki_vector/messaging/response_status.proto\"\x14\n\x12\x43onnectCubeRequest\"\x8d\x01\n\x13\x43onnectCubeResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x11\n\tobject_id\x18\x03 \x01(\r\x12\x12\n\nfactory_id\x18\x04 \x01(\t\"\x17\n\x15\x43ubesAvailableRequest\"m\n\x16\x43ubesAvailableResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x13\n\x0b\x66\x61\x63tory_ids\x18\x02 \x03(\t\"\x17\n\x15\x44isconnectCubeRequest\"X\n\x16\x44isconnectCubeResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x18\n\x16\x46lashCubeLightsRequest\"Y\n\x17\x46lashCubeLightsResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x1c\n\x1a\x46orgetPreferredCubeRequest\"]\n\x1b\x46orgetPreferredCubeResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"-\n\x17SetPreferredCubeRequest\x12\x12\n\nfactory_id\x18\x01 \x01(\t\"Z\n\x18SetPreferredCubeResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\xb0\x03\n\x14SetCubeLightsRequest\x12\x11\n\tobject_id\x18\x01 \x01(\r\x12\x10\n\x08on_color\x18\x02 \x03(\r\x12\x11\n\toff_color\x18\x03 \x03(\r\x12\x14\n\x0con_period_ms\x18\x04 \x03(\r\x12\x15\n\roff_period_ms\x18\x05 \x03(\r\x12\x1f\n\x17transition_on_period_ms\x18\x06 \x03(\r\x12 \n\x18transition_off_period_ms\x18\x07 \x03(\r\x12\x0e\n\x06offset\x18\x08 \x03(\x05\x12\x15\n\rrelative_to_x\x18\t \x01(\x02\x12\x15\n\rrelative_to_y\x18\n \x01(\x02\x12\x0e\n\x06rotate\x18\x0b \x01(\x08\x12\\\n\rmake_relative\x18\x0c \x01(\x0e\x32\x45.Anki.Vector.external_interface.SetCubeLightsRequest.MakeRelativeMode\"D\n\x10MakeRelativeMode\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03OFF\x10\x01\x12\r\n\tBY_CORNER\x10\x02\x12\x0b\n\x07\x42Y_SIDE\x10\x03\"W\n\x15SetCubeLightsResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"%\n\x0fObjectAvailable\x12\x12\n\nfactory_id\x18\x01 \x01(\t\"\x92\x01\n\x15ObjectConnectionState\x12\x11\n\tobject_id\x18\x01 \x01(\r\x12\x12\n\nfactory_id\x18\x02 \x01(\t\x12?\n\x0bobject_type\x18\x03 \x01(\x0e\x32*.Anki.Vector.external_interface.ObjectType\x12\x11\n\tconnected\x18\x04 \x01(\x08\"3\n\x0bObjectMoved\x12\x11\n\ttimestamp\x18\x01 \x01(\r\x12\x11\n\tobject_id\x18\x02 \x01(\r\";\n\x13ObjectStoppedMoving\x12\x11\n\ttimestamp\x18\x01 \x01(\r\x12\x11\n\tobject_id\x18\x02 \x01(\r\"t\n\x13ObjectUpAxisChanged\x12\x11\n\ttimestamp\x18\x01 \x01(\r\x12\x11\n\tobject_id\x18\x02 \x01(\r\x12\x37\n\x07up_axis\x18\x03 \x01(\x0e\x32&.Anki.Vector.external_interface.UpAxis\"4\n\x0cObjectTapped\x12\x11\n\ttimestamp\x18\x01 \x01(\r\x12\x11\n\tobject_id\x18\x02 \x01(\r\"\xec\x02\n\x13RobotObservedObject\x12\x11\n\ttimestamp\x18\x01 \x01(\r\x12\x43\n\robject_family\x18\x02 \x01(\x0e\x32,.Anki.Vector.external_interface.ObjectFamily\x12?\n\x0bobject_type\x18\x03 \x01(\x0e\x32*.Anki.Vector.external_interface.ObjectType\x12\x11\n\tobject_id\x18\x04 \x01(\x05\x12:\n\x08img_rect\x18\x05 \x01(\x0b\x32(.Anki.Vector.external_interface.CladRect\x12\x38\n\x04pose\x18\x06 \x01(\x0b\x32*.Anki.Vector.external_interface.PoseStruct\x12 \n\x18top_face_orientation_rad\x18\x07 \x01(\x02\x12\x11\n\tis_active\x18\x08 \x01(\r\"\x14\n\x12\x43ubeConnectionLost\"d\n\x1a\x44\x65leteCustomObjectsRequest\x12\x46\n\x04mode\x18\x01 \x01(\x0e\x32\x38.Anki.Vector.external_interface.CustomObjectDeletionMode\"]\n\x1b\x44\x65leteCustomObjectsResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\"\x93\x01\n\x1e\x43reateFixedCustomObjectRequest\x12\x38\n\x04pose\x18\x01 \x01(\x0b\x32*.Anki.Vector.external_interface.PoseStruct\x12\x11\n\tx_size_mm\x18\x02 \x01(\x02\x12\x11\n\ty_size_mm\x18\x03 \x01(\x02\x12\x11\n\tz_size_mm\x18\x04 \x01(\x02\"t\n\x1f\x43reateFixedCustomObjectResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x11\n\tobject_id\x18\x02 \x01(\r\"\xba\x04\n\x13\x43ustomBoxDefinition\x12H\n\x0cmarker_front\x18\x01 \x01(\x0e\x32\x32.Anki.Vector.external_interface.CustomObjectMarker\x12G\n\x0bmarker_back\x18\x02 \x01(\x0e\x32\x32.Anki.Vector.external_interface.CustomObjectMarker\x12\x46\n\nmarker_top\x18\x03 \x01(\x0e\x32\x32.Anki.Vector.external_interface.CustomObjectMarker\x12I\n\rmarker_bottom\x18\x04 \x01(\x0e\x32\x32.Anki.Vector.external_interface.CustomObjectMarker\x12G\n\x0bmarker_left\x18\x05 \x01(\x0e\x32\x32.Anki.Vector.external_interface.CustomObjectMarker\x12H\n\x0cmarker_right\x18\x06 \x01(\x0e\x32\x32.Anki.Vector.external_interface.CustomObjectMarker\x12\x11\n\tx_size_mm\x18\x07 \x01(\x02\x12\x11\n\ty_size_mm\x18\x08 \x01(\x02\x12\x11\n\tz_size_mm\x18\t \x01(\x02\x12\x17\n\x0fmarker_width_mm\x18\n \x01(\x02\x12\x18\n\x10marker_height_mm\x18\x0b \x01(\x02\"\x9e\x01\n\x14\x43ustomCubeDefinition\x12\x42\n\x06marker\x18\x01 \x01(\x0e\x32\x32.Anki.Vector.external_interface.CustomObjectMarker\x12\x0f\n\x07size_mm\x18\x02 \x01(\x02\x12\x17\n\x0fmarker_width_mm\x18\x03 \x01(\x02\x12\x18\n\x10marker_height_mm\x18\x04 \x01(\x02\"\xb2\x01\n\x14\x43ustomWallDefinition\x12\x42\n\x06marker\x18\x01 \x01(\x0e\x32\x32.Anki.Vector.external_interface.CustomObjectMarker\x12\x10\n\x08width_mm\x18\x02 \x01(\x02\x12\x11\n\theight_mm\x18\x03 \x01(\x02\x12\x17\n\x0fmarker_width_mm\x18\x04 \x01(\x02\x12\x18\n\x10marker_height_mm\x18\x05 \x01(\x02\"\xf0\x02\n\x19\x44\x65\x66ineCustomObjectRequest\x12?\n\x0b\x63ustom_type\x18\x01 \x01(\x0e\x32*.Anki.Vector.external_interface.CustomType\x12\x11\n\tis_unique\x18\x02 \x01(\x08\x12I\n\ncustom_box\x18\x03 \x01(\x0b\x32\x33.Anki.Vector.external_interface.CustomBoxDefinitionH\x00\x12K\n\x0b\x63ustom_cube\x18\x04 \x01(\x0b\x32\x34.Anki.Vector.external_interface.CustomCubeDefinitionH\x00\x12K\n\x0b\x63ustom_wall\x18\x05 \x01(\x0b\x32\x34.Anki.Vector.external_interface.CustomWallDefinitionH\x00\x42\x1a\n\x18\x63ustom_object_definition\"m\n\x1a\x44\x65\x66ineCustomObjectResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x0f\n\x07success\x18\x02 \x01(\x08\"\xac\x05\n\x0bObjectEvent\x12K\n\x10object_available\x18\x01 \x01(\x0b\x32/.Anki.Vector.external_interface.ObjectAvailableH\x00\x12X\n\x17object_connection_state\x18\x02 \x01(\x0b\x32\x35.Anki.Vector.external_interface.ObjectConnectionStateH\x00\x12\x43\n\x0cobject_moved\x18\x03 \x01(\x0b\x32+.Anki.Vector.external_interface.ObjectMovedH\x00\x12T\n\x15object_stopped_moving\x18\x04 \x01(\x0b\x32\x33.Anki.Vector.external_interface.ObjectStoppedMovingH\x00\x12U\n\x16object_up_axis_changed\x18\x05 \x01(\x0b\x32\x33.Anki.Vector.external_interface.ObjectUpAxisChangedH\x00\x12\x45\n\robject_tapped\x18\x06 \x01(\x0b\x32,.Anki.Vector.external_interface.ObjectTappedH\x00\x12T\n\x15robot_observed_object\x18\x07 \x01(\x0b\x32\x33.Anki.Vector.external_interface.RobotObservedObjectH\x00\x12R\n\x14\x63ube_connection_lost\x18\x08 \x01(\x0b\x32\x32.Anki.Vector.external_interface.CubeConnectionLostH\x00\x42\x13\n\x11object_event_type*\x7f\n\nObjectType\x12\x12\n\x0eINVALID_OBJECT\x10\x00\x12\x12\n\x0eUNKNOWN_OBJECT\x10\x01\x12\x14\n\x10\x42LOCK_LIGHTCUBE1\x10\x02\x12\x1c\n\x18\x46IRST_CUSTOM_OBJECT_TYPE\x10\x0f\x12\x15\n\x11OBJECT_TYPE_COUNT\x10&*\xd0\x03\n\nCustomType\x12\x17\n\x13INVALID_CUSTOM_TYPE\x10\x00\x12\x12\n\x0e\x43USTOM_TYPE_00\x10\x01\x12\x12\n\x0e\x43USTOM_TYPE_01\x10\x02\x12\x12\n\x0e\x43USTOM_TYPE_02\x10\x03\x12\x12\n\x0e\x43USTOM_TYPE_03\x10\x04\x12\x12\n\x0e\x43USTOM_TYPE_04\x10\x05\x12\x12\n\x0e\x43USTOM_TYPE_05\x10\x06\x12\x12\n\x0e\x43USTOM_TYPE_06\x10\x07\x12\x12\n\x0e\x43USTOM_TYPE_07\x10\x08\x12\x12\n\x0e\x43USTOM_TYPE_08\x10\t\x12\x12\n\x0e\x43USTOM_TYPE_09\x10\n\x12\x12\n\x0e\x43USTOM_TYPE_10\x10\x0b\x12\x12\n\x0e\x43USTOM_TYPE_11\x10\x0c\x12\x12\n\x0e\x43USTOM_TYPE_12\x10\r\x12\x12\n\x0e\x43USTOM_TYPE_13\x10\x0e\x12\x12\n\x0e\x43USTOM_TYPE_14\x10\x0f\x12\x12\n\x0e\x43USTOM_TYPE_15\x10\x10\x12\x12\n\x0e\x43USTOM_TYPE_16\x10\x11\x12\x12\n\x0e\x43USTOM_TYPE_17\x10\x12\x12\x12\n\x0e\x43USTOM_TYPE_18\x10\x13\x12\x12\n\x0e\x43USTOM_TYPE_19\x10\x14\x12\x15\n\x11\x43USTOM_TYPE_COUNT\x10\x14\x1a\x02\x10\x01*\x8e\x01\n\x0cObjectFamily\x12\x12\n\x0eINVALID_FAMILY\x10\x00\x12\x12\n\x0eUNKNOWN_FAMILY\x10\x01\x12\t\n\x05\x42LOCK\x10\x02\x12\x0e\n\nLIGHT_CUBE\x10\x03\x12\x0b\n\x07\x43HARGER\x10\x04\x12\x11\n\rCUSTOM_OBJECT\x10\x07\x12\x17\n\x13OBJECT_FAMILY_COUNT\x10\x07\x1a\x02\x10\x01*\x88\x01\n\x06UpAxis\x12\x10\n\x0cINVALID_AXIS\x10\x00\x12\x0e\n\nX_NEGATIVE\x10\x01\x12\x0e\n\nX_POSITIVE\x10\x02\x12\x0e\n\nY_NEGATIVE\x10\x03\x12\x0e\n\nY_POSITIVE\x10\x04\x12\x0e\n\nZ_NEGATIVE\x10\x05\x12\x0e\n\nZ_POSITIVE\x10\x06\x12\x0c\n\x08NUM_AXES\x10\x07*P\n\x0fObjectConstants\x12\x19\n\x15OBJECT_CONSTANTS_NULL\x10\x00\x12\"\n\x1e\x46IXED_CUSTOM_WALL_THICKNESS_MM\x10\n*\xac\x04\n\x12\x43ustomObjectMarker\x12\x19\n\x15\x43USTOM_MARKER_UNKNOWN\x10\x00\x12\x1b\n\x17\x43USTOM_MARKER_CIRCLES_2\x10\x01\x12\x1b\n\x17\x43USTOM_MARKER_CIRCLES_3\x10\x02\x12\x1b\n\x17\x43USTOM_MARKER_CIRCLES_4\x10\x03\x12\x1b\n\x17\x43USTOM_MARKER_CIRCLES_5\x10\x04\x12\x1c\n\x18\x43USTOM_MARKER_DIAMONDS_2\x10\x05\x12\x1c\n\x18\x43USTOM_MARKER_DIAMONDS_3\x10\x06\x12\x1c\n\x18\x43USTOM_MARKER_DIAMONDS_4\x10\x07\x12\x1c\n\x18\x43USTOM_MARKER_DIAMONDS_5\x10\x08\x12\x1c\n\x18\x43USTOM_MARKER_HEXAGONS_2\x10\t\x12\x1c\n\x18\x43USTOM_MARKER_HEXAGONS_3\x10\n\x12\x1c\n\x18\x43USTOM_MARKER_HEXAGONS_4\x10\x0b\x12\x1c\n\x18\x43USTOM_MARKER_HEXAGONS_5\x10\x0c\x12\x1d\n\x19\x43USTOM_MARKER_TRIANGLES_2\x10\r\x12\x1d\n\x19\x43USTOM_MARKER_TRIANGLES_3\x10\x0e\x12\x1d\n\x19\x43USTOM_MARKER_TRIANGLES_4\x10\x0f\x12\x1d\n\x19\x43USTOM_MARKER_TRIANGLES_5\x10\x10\x12\x17\n\x13\x43USTOM_MARKER_COUNT\x10\x10\x1a\x02\x10\x01*\xa4\x01\n\x18\x43ustomObjectDeletionMode\x12\x19\n\x15\x44\x45LETION_MASK_UNKNOWN\x10\x00\x12&\n\"DELETION_MASK_FIXED_CUSTOM_OBJECTS\x10\x01\x12\'\n#DELETION_MASK_CUSTOM_MARKER_OBJECTS\x10\x02\x12\x1c\n\x18\x44\x45LETION_MASK_ARCHETYPES\x10\x03\x62\x06proto3') - , - dependencies=[anki__vector_dot_messaging_dot_messages__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_response__status__pb2.DESCRIPTOR,]) - -_OBJECTTYPE = _descriptor.EnumDescriptor( - name='ObjectType', - full_name='Anki.Vector.external_interface.ObjectType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='INVALID_OBJECT', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNKNOWN_OBJECT', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BLOCK_LIGHTCUBE1', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FIRST_CUSTOM_OBJECT_TYPE', index=3, number=15, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OBJECT_TYPE_COUNT', index=4, number=38, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=4885, - serialized_end=5012, -) -_sym_db.RegisterEnumDescriptor(_OBJECTTYPE) - -ObjectType = enum_type_wrapper.EnumTypeWrapper(_OBJECTTYPE) -_CUSTOMTYPE = _descriptor.EnumDescriptor( - name='CustomType', - full_name='Anki.Vector.external_interface.CustomType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='INVALID_CUSTOM_TYPE', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_00', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_01', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_02', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_03', index=4, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_04', index=5, number=5, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_05', index=6, number=6, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_06', index=7, number=7, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_07', index=8, number=8, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_08', index=9, number=9, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_09', index=10, number=10, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_10', index=11, number=11, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_11', index=12, number=12, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_12', index=13, number=13, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_13', index=14, number=14, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_14', index=15, number=15, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_15', index=16, number=16, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_16', index=17, number=17, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_17', index=18, number=18, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_18', index=19, number=19, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_19', index=20, number=20, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_TYPE_COUNT', index=21, number=20, - options=None, - type=None), - ], - containing_type=None, - options=_descriptor._ParseOptions(descriptor_pb2.EnumOptions(), _b('\020\001')), - serialized_start=5015, - serialized_end=5479, -) -_sym_db.RegisterEnumDescriptor(_CUSTOMTYPE) - -CustomType = enum_type_wrapper.EnumTypeWrapper(_CUSTOMTYPE) -_OBJECTFAMILY = _descriptor.EnumDescriptor( - name='ObjectFamily', - full_name='Anki.Vector.external_interface.ObjectFamily', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='INVALID_FAMILY', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNKNOWN_FAMILY', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BLOCK', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='LIGHT_CUBE', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CHARGER', index=4, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_OBJECT', index=5, number=7, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OBJECT_FAMILY_COUNT', index=6, number=7, - options=None, - type=None), - ], - containing_type=None, - options=_descriptor._ParseOptions(descriptor_pb2.EnumOptions(), _b('\020\001')), - serialized_start=5482, - serialized_end=5624, -) -_sym_db.RegisterEnumDescriptor(_OBJECTFAMILY) - -ObjectFamily = enum_type_wrapper.EnumTypeWrapper(_OBJECTFAMILY) -_UPAXIS = _descriptor.EnumDescriptor( - name='UpAxis', - full_name='Anki.Vector.external_interface.UpAxis', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='INVALID_AXIS', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='X_NEGATIVE', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='X_POSITIVE', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='Y_NEGATIVE', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='Y_POSITIVE', index=4, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='Z_NEGATIVE', index=5, number=5, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='Z_POSITIVE', index=6, number=6, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NUM_AXES', index=7, number=7, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=5627, - serialized_end=5763, -) -_sym_db.RegisterEnumDescriptor(_UPAXIS) - -UpAxis = enum_type_wrapper.EnumTypeWrapper(_UPAXIS) -_OBJECTCONSTANTS = _descriptor.EnumDescriptor( - name='ObjectConstants', - full_name='Anki.Vector.external_interface.ObjectConstants', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='OBJECT_CONSTANTS_NULL', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FIXED_CUSTOM_WALL_THICKNESS_MM', index=1, number=10, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=5765, - serialized_end=5845, -) -_sym_db.RegisterEnumDescriptor(_OBJECTCONSTANTS) - -ObjectConstants = enum_type_wrapper.EnumTypeWrapper(_OBJECTCONSTANTS) -_CUSTOMOBJECTMARKER = _descriptor.EnumDescriptor( - name='CustomObjectMarker', - full_name='Anki.Vector.external_interface.CustomObjectMarker', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_CIRCLES_2', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_CIRCLES_3', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_CIRCLES_4', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_CIRCLES_5', index=4, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_DIAMONDS_2', index=5, number=5, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_DIAMONDS_3', index=6, number=6, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_DIAMONDS_4', index=7, number=7, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_DIAMONDS_5', index=8, number=8, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_HEXAGONS_2', index=9, number=9, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_HEXAGONS_3', index=10, number=10, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_HEXAGONS_4', index=11, number=11, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_HEXAGONS_5', index=12, number=12, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_TRIANGLES_2', index=13, number=13, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_TRIANGLES_3', index=14, number=14, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_TRIANGLES_4', index=15, number=15, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_TRIANGLES_5', index=16, number=16, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CUSTOM_MARKER_COUNT', index=17, number=16, - options=None, - type=None), - ], - containing_type=None, - options=_descriptor._ParseOptions(descriptor_pb2.EnumOptions(), _b('\020\001')), - serialized_start=5848, - serialized_end=6404, -) -_sym_db.RegisterEnumDescriptor(_CUSTOMOBJECTMARKER) - -CustomObjectMarker = enum_type_wrapper.EnumTypeWrapper(_CUSTOMOBJECTMARKER) -_CUSTOMOBJECTDELETIONMODE = _descriptor.EnumDescriptor( - name='CustomObjectDeletionMode', - full_name='Anki.Vector.external_interface.CustomObjectDeletionMode', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='DELETION_MASK_UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DELETION_MASK_FIXED_CUSTOM_OBJECTS', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DELETION_MASK_CUSTOM_MARKER_OBJECTS', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DELETION_MASK_ARCHETYPES', index=3, number=3, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=6407, - serialized_end=6571, -) -_sym_db.RegisterEnumDescriptor(_CUSTOMOBJECTDELETIONMODE) - -CustomObjectDeletionMode = enum_type_wrapper.EnumTypeWrapper(_CUSTOMOBJECTDELETIONMODE) -INVALID_OBJECT = 0 -UNKNOWN_OBJECT = 1 -BLOCK_LIGHTCUBE1 = 2 -FIRST_CUSTOM_OBJECT_TYPE = 15 -OBJECT_TYPE_COUNT = 38 -INVALID_CUSTOM_TYPE = 0 -CUSTOM_TYPE_00 = 1 -CUSTOM_TYPE_01 = 2 -CUSTOM_TYPE_02 = 3 -CUSTOM_TYPE_03 = 4 -CUSTOM_TYPE_04 = 5 -CUSTOM_TYPE_05 = 6 -CUSTOM_TYPE_06 = 7 -CUSTOM_TYPE_07 = 8 -CUSTOM_TYPE_08 = 9 -CUSTOM_TYPE_09 = 10 -CUSTOM_TYPE_10 = 11 -CUSTOM_TYPE_11 = 12 -CUSTOM_TYPE_12 = 13 -CUSTOM_TYPE_13 = 14 -CUSTOM_TYPE_14 = 15 -CUSTOM_TYPE_15 = 16 -CUSTOM_TYPE_16 = 17 -CUSTOM_TYPE_17 = 18 -CUSTOM_TYPE_18 = 19 -CUSTOM_TYPE_19 = 20 -CUSTOM_TYPE_COUNT = 20 -INVALID_FAMILY = 0 -UNKNOWN_FAMILY = 1 -BLOCK = 2 -LIGHT_CUBE = 3 -CHARGER = 4 -CUSTOM_OBJECT = 7 -OBJECT_FAMILY_COUNT = 7 -INVALID_AXIS = 0 -X_NEGATIVE = 1 -X_POSITIVE = 2 -Y_NEGATIVE = 3 -Y_POSITIVE = 4 -Z_NEGATIVE = 5 -Z_POSITIVE = 6 -NUM_AXES = 7 -OBJECT_CONSTANTS_NULL = 0 -FIXED_CUSTOM_WALL_THICKNESS_MM = 10 -CUSTOM_MARKER_UNKNOWN = 0 -CUSTOM_MARKER_CIRCLES_2 = 1 -CUSTOM_MARKER_CIRCLES_3 = 2 -CUSTOM_MARKER_CIRCLES_4 = 3 -CUSTOM_MARKER_CIRCLES_5 = 4 -CUSTOM_MARKER_DIAMONDS_2 = 5 -CUSTOM_MARKER_DIAMONDS_3 = 6 -CUSTOM_MARKER_DIAMONDS_4 = 7 -CUSTOM_MARKER_DIAMONDS_5 = 8 -CUSTOM_MARKER_HEXAGONS_2 = 9 -CUSTOM_MARKER_HEXAGONS_3 = 10 -CUSTOM_MARKER_HEXAGONS_4 = 11 -CUSTOM_MARKER_HEXAGONS_5 = 12 -CUSTOM_MARKER_TRIANGLES_2 = 13 -CUSTOM_MARKER_TRIANGLES_3 = 14 -CUSTOM_MARKER_TRIANGLES_4 = 15 -CUSTOM_MARKER_TRIANGLES_5 = 16 -CUSTOM_MARKER_COUNT = 16 -DELETION_MASK_UNKNOWN = 0 -DELETION_MASK_FIXED_CUSTOM_OBJECTS = 1 -DELETION_MASK_CUSTOM_MARKER_OBJECTS = 2 -DELETION_MASK_ARCHETYPES = 3 - - -_SETCUBELIGHTSREQUEST_MAKERELATIVEMODE = _descriptor.EnumDescriptor( - name='MakeRelativeMode', - full_name='Anki.Vector.external_interface.SetCubeLightsRequest.MakeRelativeMode', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OFF', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BY_CORNER', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BY_SIDE', index=3, number=3, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=1314, - serialized_end=1382, -) -_sym_db.RegisterEnumDescriptor(_SETCUBELIGHTSREQUEST_MAKERELATIVEMODE) - - -_CONNECTCUBEREQUEST = _descriptor.Descriptor( - name='ConnectCubeRequest', - full_name='Anki.Vector.external_interface.ConnectCubeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=151, - serialized_end=171, -) - - -_CONNECTCUBERESPONSE = _descriptor.Descriptor( - name='ConnectCubeResponse', - full_name='Anki.Vector.external_interface.ConnectCubeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.ConnectCubeResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='success', full_name='Anki.Vector.external_interface.ConnectCubeResponse.success', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.ConnectCubeResponse.object_id', index=2, - number=3, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='factory_id', full_name='Anki.Vector.external_interface.ConnectCubeResponse.factory_id', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=174, - serialized_end=315, -) - - -_CUBESAVAILABLEREQUEST = _descriptor.Descriptor( - name='CubesAvailableRequest', - full_name='Anki.Vector.external_interface.CubesAvailableRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=317, - serialized_end=340, -) - - -_CUBESAVAILABLERESPONSE = _descriptor.Descriptor( - name='CubesAvailableResponse', - full_name='Anki.Vector.external_interface.CubesAvailableResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.CubesAvailableResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='factory_ids', full_name='Anki.Vector.external_interface.CubesAvailableResponse.factory_ids', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=342, - serialized_end=451, -) - - -_DISCONNECTCUBEREQUEST = _descriptor.Descriptor( - name='DisconnectCubeRequest', - full_name='Anki.Vector.external_interface.DisconnectCubeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=453, - serialized_end=476, -) - - -_DISCONNECTCUBERESPONSE = _descriptor.Descriptor( - name='DisconnectCubeResponse', - full_name='Anki.Vector.external_interface.DisconnectCubeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DisconnectCubeResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=478, - serialized_end=566, -) - - -_FLASHCUBELIGHTSREQUEST = _descriptor.Descriptor( - name='FlashCubeLightsRequest', - full_name='Anki.Vector.external_interface.FlashCubeLightsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=568, - serialized_end=592, -) - - -_FLASHCUBELIGHTSRESPONSE = _descriptor.Descriptor( - name='FlashCubeLightsResponse', - full_name='Anki.Vector.external_interface.FlashCubeLightsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.FlashCubeLightsResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=594, - serialized_end=683, -) - - -_FORGETPREFERREDCUBEREQUEST = _descriptor.Descriptor( - name='ForgetPreferredCubeRequest', - full_name='Anki.Vector.external_interface.ForgetPreferredCubeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=685, - serialized_end=713, -) - - -_FORGETPREFERREDCUBERESPONSE = _descriptor.Descriptor( - name='ForgetPreferredCubeResponse', - full_name='Anki.Vector.external_interface.ForgetPreferredCubeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.ForgetPreferredCubeResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=715, - serialized_end=808, -) - - -_SETPREFERREDCUBEREQUEST = _descriptor.Descriptor( - name='SetPreferredCubeRequest', - full_name='Anki.Vector.external_interface.SetPreferredCubeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='factory_id', full_name='Anki.Vector.external_interface.SetPreferredCubeRequest.factory_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=810, - serialized_end=855, -) - - -_SETPREFERREDCUBERESPONSE = _descriptor.Descriptor( - name='SetPreferredCubeResponse', - full_name='Anki.Vector.external_interface.SetPreferredCubeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.SetPreferredCubeResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=857, - serialized_end=947, -) - - -_SETCUBELIGHTSREQUEST = _descriptor.Descriptor( - name='SetCubeLightsRequest', - full_name='Anki.Vector.external_interface.SetCubeLightsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.object_id', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='on_color', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.on_color', index=1, - number=2, type=13, cpp_type=3, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='off_color', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.off_color', index=2, - number=3, type=13, cpp_type=3, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='on_period_ms', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.on_period_ms', index=3, - number=4, type=13, cpp_type=3, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='off_period_ms', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.off_period_ms', index=4, - number=5, type=13, cpp_type=3, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transition_on_period_ms', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.transition_on_period_ms', index=5, - number=6, type=13, cpp_type=3, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transition_off_period_ms', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.transition_off_period_ms', index=6, - number=7, type=13, cpp_type=3, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='offset', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.offset', index=7, - number=8, type=5, cpp_type=1, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='relative_to_x', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.relative_to_x', index=8, - number=9, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='relative_to_y', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.relative_to_y', index=9, - number=10, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='rotate', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.rotate', index=10, - number=11, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='make_relative', full_name='Anki.Vector.external_interface.SetCubeLightsRequest.make_relative', index=11, - number=12, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _SETCUBELIGHTSREQUEST_MAKERELATIVEMODE, - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=950, - serialized_end=1382, -) - - -_SETCUBELIGHTSRESPONSE = _descriptor.Descriptor( - name='SetCubeLightsResponse', - full_name='Anki.Vector.external_interface.SetCubeLightsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.SetCubeLightsResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1384, - serialized_end=1471, -) - - -_OBJECTAVAILABLE = _descriptor.Descriptor( - name='ObjectAvailable', - full_name='Anki.Vector.external_interface.ObjectAvailable', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='factory_id', full_name='Anki.Vector.external_interface.ObjectAvailable.factory_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1473, - serialized_end=1510, -) - - -_OBJECTCONNECTIONSTATE = _descriptor.Descriptor( - name='ObjectConnectionState', - full_name='Anki.Vector.external_interface.ObjectConnectionState', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.ObjectConnectionState.object_id', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='factory_id', full_name='Anki.Vector.external_interface.ObjectConnectionState.factory_id', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_type', full_name='Anki.Vector.external_interface.ObjectConnectionState.object_type', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='connected', full_name='Anki.Vector.external_interface.ObjectConnectionState.connected', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1513, - serialized_end=1659, -) - - -_OBJECTMOVED = _descriptor.Descriptor( - name='ObjectMoved', - full_name='Anki.Vector.external_interface.ObjectMoved', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='timestamp', full_name='Anki.Vector.external_interface.ObjectMoved.timestamp', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.ObjectMoved.object_id', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1661, - serialized_end=1712, -) - - -_OBJECTSTOPPEDMOVING = _descriptor.Descriptor( - name='ObjectStoppedMoving', - full_name='Anki.Vector.external_interface.ObjectStoppedMoving', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='timestamp', full_name='Anki.Vector.external_interface.ObjectStoppedMoving.timestamp', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.ObjectStoppedMoving.object_id', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1714, - serialized_end=1773, -) - - -_OBJECTUPAXISCHANGED = _descriptor.Descriptor( - name='ObjectUpAxisChanged', - full_name='Anki.Vector.external_interface.ObjectUpAxisChanged', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='timestamp', full_name='Anki.Vector.external_interface.ObjectUpAxisChanged.timestamp', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.ObjectUpAxisChanged.object_id', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='up_axis', full_name='Anki.Vector.external_interface.ObjectUpAxisChanged.up_axis', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1775, - serialized_end=1891, -) - - -_OBJECTTAPPED = _descriptor.Descriptor( - name='ObjectTapped', - full_name='Anki.Vector.external_interface.ObjectTapped', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='timestamp', full_name='Anki.Vector.external_interface.ObjectTapped.timestamp', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.ObjectTapped.object_id', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1893, - serialized_end=1945, -) - - -_ROBOTOBSERVEDOBJECT = _descriptor.Descriptor( - name='RobotObservedObject', - full_name='Anki.Vector.external_interface.RobotObservedObject', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='timestamp', full_name='Anki.Vector.external_interface.RobotObservedObject.timestamp', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_family', full_name='Anki.Vector.external_interface.RobotObservedObject.object_family', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_type', full_name='Anki.Vector.external_interface.RobotObservedObject.object_type', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.RobotObservedObject.object_id', index=3, - number=4, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='img_rect', full_name='Anki.Vector.external_interface.RobotObservedObject.img_rect', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='pose', full_name='Anki.Vector.external_interface.RobotObservedObject.pose', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='top_face_orientation_rad', full_name='Anki.Vector.external_interface.RobotObservedObject.top_face_orientation_rad', index=6, - number=7, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_active', full_name='Anki.Vector.external_interface.RobotObservedObject.is_active', index=7, - number=8, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1948, - serialized_end=2312, -) - - -_CUBECONNECTIONLOST = _descriptor.Descriptor( - name='CubeConnectionLost', - full_name='Anki.Vector.external_interface.CubeConnectionLost', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2314, - serialized_end=2334, -) - - -_DELETECUSTOMOBJECTSREQUEST = _descriptor.Descriptor( - name='DeleteCustomObjectsRequest', - full_name='Anki.Vector.external_interface.DeleteCustomObjectsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='mode', full_name='Anki.Vector.external_interface.DeleteCustomObjectsRequest.mode', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2336, - serialized_end=2436, -) - - -_DELETECUSTOMOBJECTSRESPONSE = _descriptor.Descriptor( - name='DeleteCustomObjectsResponse', - full_name='Anki.Vector.external_interface.DeleteCustomObjectsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DeleteCustomObjectsResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2438, - serialized_end=2531, -) - - -_CREATEFIXEDCUSTOMOBJECTREQUEST = _descriptor.Descriptor( - name='CreateFixedCustomObjectRequest', - full_name='Anki.Vector.external_interface.CreateFixedCustomObjectRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='pose', full_name='Anki.Vector.external_interface.CreateFixedCustomObjectRequest.pose', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='x_size_mm', full_name='Anki.Vector.external_interface.CreateFixedCustomObjectRequest.x_size_mm', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='y_size_mm', full_name='Anki.Vector.external_interface.CreateFixedCustomObjectRequest.y_size_mm', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='z_size_mm', full_name='Anki.Vector.external_interface.CreateFixedCustomObjectRequest.z_size_mm', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2534, - serialized_end=2681, -) - - -_CREATEFIXEDCUSTOMOBJECTRESPONSE = _descriptor.Descriptor( - name='CreateFixedCustomObjectResponse', - full_name='Anki.Vector.external_interface.CreateFixedCustomObjectResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.CreateFixedCustomObjectResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.CreateFixedCustomObjectResponse.object_id', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2683, - serialized_end=2799, -) - - -_CUSTOMBOXDEFINITION = _descriptor.Descriptor( - name='CustomBoxDefinition', - full_name='Anki.Vector.external_interface.CustomBoxDefinition', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='marker_front', full_name='Anki.Vector.external_interface.CustomBoxDefinition.marker_front', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_back', full_name='Anki.Vector.external_interface.CustomBoxDefinition.marker_back', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_top', full_name='Anki.Vector.external_interface.CustomBoxDefinition.marker_top', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_bottom', full_name='Anki.Vector.external_interface.CustomBoxDefinition.marker_bottom', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_left', full_name='Anki.Vector.external_interface.CustomBoxDefinition.marker_left', index=4, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_right', full_name='Anki.Vector.external_interface.CustomBoxDefinition.marker_right', index=5, - number=6, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='x_size_mm', full_name='Anki.Vector.external_interface.CustomBoxDefinition.x_size_mm', index=6, - number=7, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='y_size_mm', full_name='Anki.Vector.external_interface.CustomBoxDefinition.y_size_mm', index=7, - number=8, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='z_size_mm', full_name='Anki.Vector.external_interface.CustomBoxDefinition.z_size_mm', index=8, - number=9, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_width_mm', full_name='Anki.Vector.external_interface.CustomBoxDefinition.marker_width_mm', index=9, - number=10, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_height_mm', full_name='Anki.Vector.external_interface.CustomBoxDefinition.marker_height_mm', index=10, - number=11, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2802, - serialized_end=3372, -) - - -_CUSTOMCUBEDEFINITION = _descriptor.Descriptor( - name='CustomCubeDefinition', - full_name='Anki.Vector.external_interface.CustomCubeDefinition', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='marker', full_name='Anki.Vector.external_interface.CustomCubeDefinition.marker', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='size_mm', full_name='Anki.Vector.external_interface.CustomCubeDefinition.size_mm', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_width_mm', full_name='Anki.Vector.external_interface.CustomCubeDefinition.marker_width_mm', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_height_mm', full_name='Anki.Vector.external_interface.CustomCubeDefinition.marker_height_mm', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3375, - serialized_end=3533, -) - - -_CUSTOMWALLDEFINITION = _descriptor.Descriptor( - name='CustomWallDefinition', - full_name='Anki.Vector.external_interface.CustomWallDefinition', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='marker', full_name='Anki.Vector.external_interface.CustomWallDefinition.marker', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='width_mm', full_name='Anki.Vector.external_interface.CustomWallDefinition.width_mm', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='height_mm', full_name='Anki.Vector.external_interface.CustomWallDefinition.height_mm', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_width_mm', full_name='Anki.Vector.external_interface.CustomWallDefinition.marker_width_mm', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='marker_height_mm', full_name='Anki.Vector.external_interface.CustomWallDefinition.marker_height_mm', index=4, - number=5, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3536, - serialized_end=3714, -) - - -_DEFINECUSTOMOBJECTREQUEST = _descriptor.Descriptor( - name='DefineCustomObjectRequest', - full_name='Anki.Vector.external_interface.DefineCustomObjectRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='custom_type', full_name='Anki.Vector.external_interface.DefineCustomObjectRequest.custom_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_unique', full_name='Anki.Vector.external_interface.DefineCustomObjectRequest.is_unique', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='custom_box', full_name='Anki.Vector.external_interface.DefineCustomObjectRequest.custom_box', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='custom_cube', full_name='Anki.Vector.external_interface.DefineCustomObjectRequest.custom_cube', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='custom_wall', full_name='Anki.Vector.external_interface.DefineCustomObjectRequest.custom_wall', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - _descriptor.OneofDescriptor( - name='custom_object_definition', full_name='Anki.Vector.external_interface.DefineCustomObjectRequest.custom_object_definition', - index=0, containing_type=None, fields=[]), - ], - serialized_start=3717, - serialized_end=4085, -) - - -_DEFINECUSTOMOBJECTRESPONSE = _descriptor.Descriptor( - name='DefineCustomObjectResponse', - full_name='Anki.Vector.external_interface.DefineCustomObjectResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DefineCustomObjectResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='success', full_name='Anki.Vector.external_interface.DefineCustomObjectResponse.success', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4087, - serialized_end=4196, -) - - -_OBJECTEVENT = _descriptor.Descriptor( - name='ObjectEvent', - full_name='Anki.Vector.external_interface.ObjectEvent', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_available', full_name='Anki.Vector.external_interface.ObjectEvent.object_available', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_connection_state', full_name='Anki.Vector.external_interface.ObjectEvent.object_connection_state', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_moved', full_name='Anki.Vector.external_interface.ObjectEvent.object_moved', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_stopped_moving', full_name='Anki.Vector.external_interface.ObjectEvent.object_stopped_moving', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_up_axis_changed', full_name='Anki.Vector.external_interface.ObjectEvent.object_up_axis_changed', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_tapped', full_name='Anki.Vector.external_interface.ObjectEvent.object_tapped', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='robot_observed_object', full_name='Anki.Vector.external_interface.ObjectEvent.robot_observed_object', index=6, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cube_connection_lost', full_name='Anki.Vector.external_interface.ObjectEvent.cube_connection_lost', index=7, - number=8, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - _descriptor.OneofDescriptor( - name='object_event_type', full_name='Anki.Vector.external_interface.ObjectEvent.object_event_type', - index=0, containing_type=None, fields=[]), - ], - serialized_start=4199, - serialized_end=4883, -) - -_CONNECTCUBERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_CUBESAVAILABLERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_DISCONNECTCUBERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_FLASHCUBELIGHTSRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_FORGETPREFERREDCUBERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_SETPREFERREDCUBERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_SETCUBELIGHTSREQUEST.fields_by_name['make_relative'].enum_type = _SETCUBELIGHTSREQUEST_MAKERELATIVEMODE -_SETCUBELIGHTSREQUEST_MAKERELATIVEMODE.containing_type = _SETCUBELIGHTSREQUEST -_SETCUBELIGHTSRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_OBJECTCONNECTIONSTATE.fields_by_name['object_type'].enum_type = _OBJECTTYPE -_OBJECTUPAXISCHANGED.fields_by_name['up_axis'].enum_type = _UPAXIS -_ROBOTOBSERVEDOBJECT.fields_by_name['object_family'].enum_type = _OBJECTFAMILY -_ROBOTOBSERVEDOBJECT.fields_by_name['object_type'].enum_type = _OBJECTTYPE -_ROBOTOBSERVEDOBJECT.fields_by_name['img_rect'].message_type = anki__vector_dot_messaging_dot_messages__pb2._CLADRECT -_ROBOTOBSERVEDOBJECT.fields_by_name['pose'].message_type = anki__vector_dot_messaging_dot_messages__pb2._POSESTRUCT -_DELETECUSTOMOBJECTSREQUEST.fields_by_name['mode'].enum_type = _CUSTOMOBJECTDELETIONMODE -_DELETECUSTOMOBJECTSRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_CREATEFIXEDCUSTOMOBJECTREQUEST.fields_by_name['pose'].message_type = anki__vector_dot_messaging_dot_messages__pb2._POSESTRUCT -_CREATEFIXEDCUSTOMOBJECTRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_CUSTOMBOXDEFINITION.fields_by_name['marker_front'].enum_type = _CUSTOMOBJECTMARKER -_CUSTOMBOXDEFINITION.fields_by_name['marker_back'].enum_type = _CUSTOMOBJECTMARKER -_CUSTOMBOXDEFINITION.fields_by_name['marker_top'].enum_type = _CUSTOMOBJECTMARKER -_CUSTOMBOXDEFINITION.fields_by_name['marker_bottom'].enum_type = _CUSTOMOBJECTMARKER -_CUSTOMBOXDEFINITION.fields_by_name['marker_left'].enum_type = _CUSTOMOBJECTMARKER -_CUSTOMBOXDEFINITION.fields_by_name['marker_right'].enum_type = _CUSTOMOBJECTMARKER -_CUSTOMCUBEDEFINITION.fields_by_name['marker'].enum_type = _CUSTOMOBJECTMARKER -_CUSTOMWALLDEFINITION.fields_by_name['marker'].enum_type = _CUSTOMOBJECTMARKER -_DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_type'].enum_type = _CUSTOMTYPE -_DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_box'].message_type = _CUSTOMBOXDEFINITION -_DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_cube'].message_type = _CUSTOMCUBEDEFINITION -_DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_wall'].message_type = _CUSTOMWALLDEFINITION -_DEFINECUSTOMOBJECTREQUEST.oneofs_by_name['custom_object_definition'].fields.append( - _DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_box']) -_DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_box'].containing_oneof = _DEFINECUSTOMOBJECTREQUEST.oneofs_by_name['custom_object_definition'] -_DEFINECUSTOMOBJECTREQUEST.oneofs_by_name['custom_object_definition'].fields.append( - _DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_cube']) -_DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_cube'].containing_oneof = _DEFINECUSTOMOBJECTREQUEST.oneofs_by_name['custom_object_definition'] -_DEFINECUSTOMOBJECTREQUEST.oneofs_by_name['custom_object_definition'].fields.append( - _DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_wall']) -_DEFINECUSTOMOBJECTREQUEST.fields_by_name['custom_wall'].containing_oneof = _DEFINECUSTOMOBJECTREQUEST.oneofs_by_name['custom_object_definition'] -_DEFINECUSTOMOBJECTRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_OBJECTEVENT.fields_by_name['object_available'].message_type = _OBJECTAVAILABLE -_OBJECTEVENT.fields_by_name['object_connection_state'].message_type = _OBJECTCONNECTIONSTATE -_OBJECTEVENT.fields_by_name['object_moved'].message_type = _OBJECTMOVED -_OBJECTEVENT.fields_by_name['object_stopped_moving'].message_type = _OBJECTSTOPPEDMOVING -_OBJECTEVENT.fields_by_name['object_up_axis_changed'].message_type = _OBJECTUPAXISCHANGED -_OBJECTEVENT.fields_by_name['object_tapped'].message_type = _OBJECTTAPPED -_OBJECTEVENT.fields_by_name['robot_observed_object'].message_type = _ROBOTOBSERVEDOBJECT -_OBJECTEVENT.fields_by_name['cube_connection_lost'].message_type = _CUBECONNECTIONLOST -_OBJECTEVENT.oneofs_by_name['object_event_type'].fields.append( - _OBJECTEVENT.fields_by_name['object_available']) -_OBJECTEVENT.fields_by_name['object_available'].containing_oneof = _OBJECTEVENT.oneofs_by_name['object_event_type'] -_OBJECTEVENT.oneofs_by_name['object_event_type'].fields.append( - _OBJECTEVENT.fields_by_name['object_connection_state']) -_OBJECTEVENT.fields_by_name['object_connection_state'].containing_oneof = _OBJECTEVENT.oneofs_by_name['object_event_type'] -_OBJECTEVENT.oneofs_by_name['object_event_type'].fields.append( - _OBJECTEVENT.fields_by_name['object_moved']) -_OBJECTEVENT.fields_by_name['object_moved'].containing_oneof = _OBJECTEVENT.oneofs_by_name['object_event_type'] -_OBJECTEVENT.oneofs_by_name['object_event_type'].fields.append( - _OBJECTEVENT.fields_by_name['object_stopped_moving']) -_OBJECTEVENT.fields_by_name['object_stopped_moving'].containing_oneof = _OBJECTEVENT.oneofs_by_name['object_event_type'] -_OBJECTEVENT.oneofs_by_name['object_event_type'].fields.append( - _OBJECTEVENT.fields_by_name['object_up_axis_changed']) -_OBJECTEVENT.fields_by_name['object_up_axis_changed'].containing_oneof = _OBJECTEVENT.oneofs_by_name['object_event_type'] -_OBJECTEVENT.oneofs_by_name['object_event_type'].fields.append( - _OBJECTEVENT.fields_by_name['object_tapped']) -_OBJECTEVENT.fields_by_name['object_tapped'].containing_oneof = _OBJECTEVENT.oneofs_by_name['object_event_type'] -_OBJECTEVENT.oneofs_by_name['object_event_type'].fields.append( - _OBJECTEVENT.fields_by_name['robot_observed_object']) -_OBJECTEVENT.fields_by_name['robot_observed_object'].containing_oneof = _OBJECTEVENT.oneofs_by_name['object_event_type'] -_OBJECTEVENT.oneofs_by_name['object_event_type'].fields.append( - _OBJECTEVENT.fields_by_name['cube_connection_lost']) -_OBJECTEVENT.fields_by_name['cube_connection_lost'].containing_oneof = _OBJECTEVENT.oneofs_by_name['object_event_type'] -DESCRIPTOR.message_types_by_name['ConnectCubeRequest'] = _CONNECTCUBEREQUEST -DESCRIPTOR.message_types_by_name['ConnectCubeResponse'] = _CONNECTCUBERESPONSE -DESCRIPTOR.message_types_by_name['CubesAvailableRequest'] = _CUBESAVAILABLEREQUEST -DESCRIPTOR.message_types_by_name['CubesAvailableResponse'] = _CUBESAVAILABLERESPONSE -DESCRIPTOR.message_types_by_name['DisconnectCubeRequest'] = _DISCONNECTCUBEREQUEST -DESCRIPTOR.message_types_by_name['DisconnectCubeResponse'] = _DISCONNECTCUBERESPONSE -DESCRIPTOR.message_types_by_name['FlashCubeLightsRequest'] = _FLASHCUBELIGHTSREQUEST -DESCRIPTOR.message_types_by_name['FlashCubeLightsResponse'] = _FLASHCUBELIGHTSRESPONSE -DESCRIPTOR.message_types_by_name['ForgetPreferredCubeRequest'] = _FORGETPREFERREDCUBEREQUEST -DESCRIPTOR.message_types_by_name['ForgetPreferredCubeResponse'] = _FORGETPREFERREDCUBERESPONSE -DESCRIPTOR.message_types_by_name['SetPreferredCubeRequest'] = _SETPREFERREDCUBEREQUEST -DESCRIPTOR.message_types_by_name['SetPreferredCubeResponse'] = _SETPREFERREDCUBERESPONSE -DESCRIPTOR.message_types_by_name['SetCubeLightsRequest'] = _SETCUBELIGHTSREQUEST -DESCRIPTOR.message_types_by_name['SetCubeLightsResponse'] = _SETCUBELIGHTSRESPONSE -DESCRIPTOR.message_types_by_name['ObjectAvailable'] = _OBJECTAVAILABLE -DESCRIPTOR.message_types_by_name['ObjectConnectionState'] = _OBJECTCONNECTIONSTATE -DESCRIPTOR.message_types_by_name['ObjectMoved'] = _OBJECTMOVED -DESCRIPTOR.message_types_by_name['ObjectStoppedMoving'] = _OBJECTSTOPPEDMOVING -DESCRIPTOR.message_types_by_name['ObjectUpAxisChanged'] = _OBJECTUPAXISCHANGED -DESCRIPTOR.message_types_by_name['ObjectTapped'] = _OBJECTTAPPED -DESCRIPTOR.message_types_by_name['RobotObservedObject'] = _ROBOTOBSERVEDOBJECT -DESCRIPTOR.message_types_by_name['CubeConnectionLost'] = _CUBECONNECTIONLOST -DESCRIPTOR.message_types_by_name['DeleteCustomObjectsRequest'] = _DELETECUSTOMOBJECTSREQUEST -DESCRIPTOR.message_types_by_name['DeleteCustomObjectsResponse'] = _DELETECUSTOMOBJECTSRESPONSE -DESCRIPTOR.message_types_by_name['CreateFixedCustomObjectRequest'] = _CREATEFIXEDCUSTOMOBJECTREQUEST -DESCRIPTOR.message_types_by_name['CreateFixedCustomObjectResponse'] = _CREATEFIXEDCUSTOMOBJECTRESPONSE -DESCRIPTOR.message_types_by_name['CustomBoxDefinition'] = _CUSTOMBOXDEFINITION -DESCRIPTOR.message_types_by_name['CustomCubeDefinition'] = _CUSTOMCUBEDEFINITION -DESCRIPTOR.message_types_by_name['CustomWallDefinition'] = _CUSTOMWALLDEFINITION -DESCRIPTOR.message_types_by_name['DefineCustomObjectRequest'] = _DEFINECUSTOMOBJECTREQUEST -DESCRIPTOR.message_types_by_name['DefineCustomObjectResponse'] = _DEFINECUSTOMOBJECTRESPONSE -DESCRIPTOR.message_types_by_name['ObjectEvent'] = _OBJECTEVENT -DESCRIPTOR.enum_types_by_name['ObjectType'] = _OBJECTTYPE -DESCRIPTOR.enum_types_by_name['CustomType'] = _CUSTOMTYPE -DESCRIPTOR.enum_types_by_name['ObjectFamily'] = _OBJECTFAMILY -DESCRIPTOR.enum_types_by_name['UpAxis'] = _UPAXIS -DESCRIPTOR.enum_types_by_name['ObjectConstants'] = _OBJECTCONSTANTS -DESCRIPTOR.enum_types_by_name['CustomObjectMarker'] = _CUSTOMOBJECTMARKER -DESCRIPTOR.enum_types_by_name['CustomObjectDeletionMode'] = _CUSTOMOBJECTDELETIONMODE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -ConnectCubeRequest = _reflection.GeneratedProtocolMessageType('ConnectCubeRequest', (_message.Message,), dict( - DESCRIPTOR = _CONNECTCUBEREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ConnectCubeRequest) - )) -_sym_db.RegisterMessage(ConnectCubeRequest) - -ConnectCubeResponse = _reflection.GeneratedProtocolMessageType('ConnectCubeResponse', (_message.Message,), dict( - DESCRIPTOR = _CONNECTCUBERESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ConnectCubeResponse) - )) -_sym_db.RegisterMessage(ConnectCubeResponse) - -CubesAvailableRequest = _reflection.GeneratedProtocolMessageType('CubesAvailableRequest', (_message.Message,), dict( - DESCRIPTOR = _CUBESAVAILABLEREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CubesAvailableRequest) - )) -_sym_db.RegisterMessage(CubesAvailableRequest) - -CubesAvailableResponse = _reflection.GeneratedProtocolMessageType('CubesAvailableResponse', (_message.Message,), dict( - DESCRIPTOR = _CUBESAVAILABLERESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CubesAvailableResponse) - )) -_sym_db.RegisterMessage(CubesAvailableResponse) - -DisconnectCubeRequest = _reflection.GeneratedProtocolMessageType('DisconnectCubeRequest', (_message.Message,), dict( - DESCRIPTOR = _DISCONNECTCUBEREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DisconnectCubeRequest) - )) -_sym_db.RegisterMessage(DisconnectCubeRequest) - -DisconnectCubeResponse = _reflection.GeneratedProtocolMessageType('DisconnectCubeResponse', (_message.Message,), dict( - DESCRIPTOR = _DISCONNECTCUBERESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DisconnectCubeResponse) - )) -_sym_db.RegisterMessage(DisconnectCubeResponse) - -FlashCubeLightsRequest = _reflection.GeneratedProtocolMessageType('FlashCubeLightsRequest', (_message.Message,), dict( - DESCRIPTOR = _FLASHCUBELIGHTSREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.FlashCubeLightsRequest) - )) -_sym_db.RegisterMessage(FlashCubeLightsRequest) - -FlashCubeLightsResponse = _reflection.GeneratedProtocolMessageType('FlashCubeLightsResponse', (_message.Message,), dict( - DESCRIPTOR = _FLASHCUBELIGHTSRESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.FlashCubeLightsResponse) - )) -_sym_db.RegisterMessage(FlashCubeLightsResponse) - -ForgetPreferredCubeRequest = _reflection.GeneratedProtocolMessageType('ForgetPreferredCubeRequest', (_message.Message,), dict( - DESCRIPTOR = _FORGETPREFERREDCUBEREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ForgetPreferredCubeRequest) - )) -_sym_db.RegisterMessage(ForgetPreferredCubeRequest) - -ForgetPreferredCubeResponse = _reflection.GeneratedProtocolMessageType('ForgetPreferredCubeResponse', (_message.Message,), dict( - DESCRIPTOR = _FORGETPREFERREDCUBERESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ForgetPreferredCubeResponse) - )) -_sym_db.RegisterMessage(ForgetPreferredCubeResponse) - -SetPreferredCubeRequest = _reflection.GeneratedProtocolMessageType('SetPreferredCubeRequest', (_message.Message,), dict( - DESCRIPTOR = _SETPREFERREDCUBEREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetPreferredCubeRequest) - )) -_sym_db.RegisterMessage(SetPreferredCubeRequest) - -SetPreferredCubeResponse = _reflection.GeneratedProtocolMessageType('SetPreferredCubeResponse', (_message.Message,), dict( - DESCRIPTOR = _SETPREFERREDCUBERESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetPreferredCubeResponse) - )) -_sym_db.RegisterMessage(SetPreferredCubeResponse) - -SetCubeLightsRequest = _reflection.GeneratedProtocolMessageType('SetCubeLightsRequest', (_message.Message,), dict( - DESCRIPTOR = _SETCUBELIGHTSREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetCubeLightsRequest) - )) -_sym_db.RegisterMessage(SetCubeLightsRequest) - -SetCubeLightsResponse = _reflection.GeneratedProtocolMessageType('SetCubeLightsResponse', (_message.Message,), dict( - DESCRIPTOR = _SETCUBELIGHTSRESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetCubeLightsResponse) - )) -_sym_db.RegisterMessage(SetCubeLightsResponse) - -ObjectAvailable = _reflection.GeneratedProtocolMessageType('ObjectAvailable', (_message.Message,), dict( - DESCRIPTOR = _OBJECTAVAILABLE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ObjectAvailable) - )) -_sym_db.RegisterMessage(ObjectAvailable) - -ObjectConnectionState = _reflection.GeneratedProtocolMessageType('ObjectConnectionState', (_message.Message,), dict( - DESCRIPTOR = _OBJECTCONNECTIONSTATE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ObjectConnectionState) - )) -_sym_db.RegisterMessage(ObjectConnectionState) - -ObjectMoved = _reflection.GeneratedProtocolMessageType('ObjectMoved', (_message.Message,), dict( - DESCRIPTOR = _OBJECTMOVED, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ObjectMoved) - )) -_sym_db.RegisterMessage(ObjectMoved) - -ObjectStoppedMoving = _reflection.GeneratedProtocolMessageType('ObjectStoppedMoving', (_message.Message,), dict( - DESCRIPTOR = _OBJECTSTOPPEDMOVING, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ObjectStoppedMoving) - )) -_sym_db.RegisterMessage(ObjectStoppedMoving) - -ObjectUpAxisChanged = _reflection.GeneratedProtocolMessageType('ObjectUpAxisChanged', (_message.Message,), dict( - DESCRIPTOR = _OBJECTUPAXISCHANGED, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ObjectUpAxisChanged) - )) -_sym_db.RegisterMessage(ObjectUpAxisChanged) - -ObjectTapped = _reflection.GeneratedProtocolMessageType('ObjectTapped', (_message.Message,), dict( - DESCRIPTOR = _OBJECTTAPPED, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ObjectTapped) - )) -_sym_db.RegisterMessage(ObjectTapped) - -RobotObservedObject = _reflection.GeneratedProtocolMessageType('RobotObservedObject', (_message.Message,), dict( - DESCRIPTOR = _ROBOTOBSERVEDOBJECT, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.RobotObservedObject) - )) -_sym_db.RegisterMessage(RobotObservedObject) - -CubeConnectionLost = _reflection.GeneratedProtocolMessageType('CubeConnectionLost', (_message.Message,), dict( - DESCRIPTOR = _CUBECONNECTIONLOST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CubeConnectionLost) - )) -_sym_db.RegisterMessage(CubeConnectionLost) - -DeleteCustomObjectsRequest = _reflection.GeneratedProtocolMessageType('DeleteCustomObjectsRequest', (_message.Message,), dict( - DESCRIPTOR = _DELETECUSTOMOBJECTSREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DeleteCustomObjectsRequest) - )) -_sym_db.RegisterMessage(DeleteCustomObjectsRequest) - -DeleteCustomObjectsResponse = _reflection.GeneratedProtocolMessageType('DeleteCustomObjectsResponse', (_message.Message,), dict( - DESCRIPTOR = _DELETECUSTOMOBJECTSRESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DeleteCustomObjectsResponse) - )) -_sym_db.RegisterMessage(DeleteCustomObjectsResponse) - -CreateFixedCustomObjectRequest = _reflection.GeneratedProtocolMessageType('CreateFixedCustomObjectRequest', (_message.Message,), dict( - DESCRIPTOR = _CREATEFIXEDCUSTOMOBJECTREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CreateFixedCustomObjectRequest) - )) -_sym_db.RegisterMessage(CreateFixedCustomObjectRequest) - -CreateFixedCustomObjectResponse = _reflection.GeneratedProtocolMessageType('CreateFixedCustomObjectResponse', (_message.Message,), dict( - DESCRIPTOR = _CREATEFIXEDCUSTOMOBJECTRESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CreateFixedCustomObjectResponse) - )) -_sym_db.RegisterMessage(CreateFixedCustomObjectResponse) - -CustomBoxDefinition = _reflection.GeneratedProtocolMessageType('CustomBoxDefinition', (_message.Message,), dict( - DESCRIPTOR = _CUSTOMBOXDEFINITION, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CustomBoxDefinition) - )) -_sym_db.RegisterMessage(CustomBoxDefinition) - -CustomCubeDefinition = _reflection.GeneratedProtocolMessageType('CustomCubeDefinition', (_message.Message,), dict( - DESCRIPTOR = _CUSTOMCUBEDEFINITION, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CustomCubeDefinition) - )) -_sym_db.RegisterMessage(CustomCubeDefinition) - -CustomWallDefinition = _reflection.GeneratedProtocolMessageType('CustomWallDefinition', (_message.Message,), dict( - DESCRIPTOR = _CUSTOMWALLDEFINITION, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CustomWallDefinition) - )) -_sym_db.RegisterMessage(CustomWallDefinition) - -DefineCustomObjectRequest = _reflection.GeneratedProtocolMessageType('DefineCustomObjectRequest', (_message.Message,), dict( - DESCRIPTOR = _DEFINECUSTOMOBJECTREQUEST, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DefineCustomObjectRequest) - )) -_sym_db.RegisterMessage(DefineCustomObjectRequest) - -DefineCustomObjectResponse = _reflection.GeneratedProtocolMessageType('DefineCustomObjectResponse', (_message.Message,), dict( - DESCRIPTOR = _DEFINECUSTOMOBJECTRESPONSE, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DefineCustomObjectResponse) - )) -_sym_db.RegisterMessage(DefineCustomObjectResponse) - -ObjectEvent = _reflection.GeneratedProtocolMessageType('ObjectEvent', (_message.Message,), dict( - DESCRIPTOR = _OBJECTEVENT, - __module__ = 'anki_vector.messaging.cube_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ObjectEvent) - )) -_sym_db.RegisterMessage(ObjectEvent) - - -_CUSTOMTYPE.has_options = True -_CUSTOMTYPE._options = _descriptor._ParseOptions(descriptor_pb2.EnumOptions(), _b('\020\001')) -_OBJECTFAMILY.has_options = True -_OBJECTFAMILY._options = _descriptor._ParseOptions(descriptor_pb2.EnumOptions(), _b('\020\001')) -_CUSTOMOBJECTMARKER.has_options = True -_CUSTOMOBJECTMARKER._options = _descriptor._ParseOptions(descriptor_pb2.EnumOptions(), _b('\020\001')) -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/cube_pb2_grpc.py b/anki_vector/messaging/cube_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/cube_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/extensions.proto b/anki_vector/messaging/extensions.proto deleted file mode 100644 index 20015ad..0000000 --- a/anki_vector/messaging/extensions.proto +++ /dev/null @@ -1,25 +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. - -// Add a decorator for streamed message handling in app - -syntax = "proto3"; - -package Anki.Vector.external_interface; - -import "google/protobuf/descriptor.proto"; - -extend google.protobuf.MessageOptions { - bool streamed = 60000; -} \ No newline at end of file diff --git a/anki_vector/messaging/extensions_pb2.py b/anki_vector/messaging/extensions_pb2.py deleted file mode 100644 index 1b5cae5..0000000 --- a/anki_vector/messaging/extensions_pb2.py +++ /dev/null @@ -1,42 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/extensions.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() - - -from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='anki_vector/messaging/extensions.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - serialized_pb=_b('\n&anki_vector/messaging/extensions.proto\x12\x1e\x41nki.Vector.external_interface\x1a google/protobuf/descriptor.proto:3\n\x08streamed\x12\x1f.google.protobuf.MessageOptions\x18\xe0\xd4\x03 \x01(\x08\x62\x06proto3') - , - dependencies=[google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,]) - - -STREAMED_FIELD_NUMBER = 60000 -streamed = _descriptor.FieldDescriptor( - name='streamed', full_name='Anki.Vector.external_interface.streamed', index=0, - number=60000, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=True, extension_scope=None, - options=None, file=DESCRIPTOR) - -DESCRIPTOR.extensions_by_name['streamed'] = streamed -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(streamed) - -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/extensions_pb2_grpc.py b/anki_vector/messaging/extensions_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/extensions_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/external_interface.proto b/anki_vector/messaging/external_interface.proto deleted file mode 100644 index 2c60104..0000000 --- a/anki_vector/messaging/external_interface.proto +++ /dev/null @@ -1,408 +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. - -// External interface for robot <-> app and robot <-> sdk communication - -syntax = "proto3"; - -package Anki.Vector.external_interface; - -import "google/api/annotations.proto"; -import "anki_vector/messaging/behavior.proto"; -import "anki_vector/messaging/cube.proto"; -import "anki_vector/messaging/messages.proto"; -import "anki_vector/messaging/nav_map.proto"; -import "anki_vector/messaging/shared.proto"; - -// The grpc-defined connection between the SDK and Anki's Vector robot. -service ExternalInterface { - // Checks the supported protocol version by passing in the client version and - // minimum host version and receiving a response to see whether the versions are supported. - rpc ProtocolVersion(ProtocolVersionRequest) returns (ProtocolVersionResponse) { - option (google.api.http) = { - post: "/v1/protocol_version", - body: "*" - }; - } - - // SDK-only message to pass version info for device OS, Python version, etc. - rpc SDKInitialization(SDKInitializationRequest) returns (SDKInitializationResponse) { - option (google.api.http) = { - post: "/v1/sdk_initialization", - body: "*" - }; - } - - // Sets the speed and acceleration for Vector's wheel motors. - rpc DriveWheels(DriveWheelsRequest) returns (DriveWheelsResponse) {} - - // Requests that Vector play an animation. - rpc PlayAnimation(PlayAnimationRequest) returns (PlayAnimationResponse) {} - - // Constructs and returns a list of animations. - rpc ListAnimations(ListAnimationsRequest) returns (ListAnimationsResponse) { - option (google.api.http) = { - post: "/v1/list_animations", - body: "*" - }; - } - - // Moves Vector's head. - rpc MoveHead(MoveHeadRequest) returns (MoveHeadResponse) {} - - // Moves Vector's lift. - rpc MoveLift(MoveLiftRequest) returns (MoveLiftResponse) {} - - // Sets screen (Vector's face) to a solid color. - rpc DisplayFaceImageRGB(DisplayFaceImageRGBRequest) returns (DisplayFaceImageRGBResponse) { - option (google.api.http) = { - post: "/v1/display_face_image_rgb", - body: "*" - }; - } - - // Streaming events endpoint - rpc EventStream(EventRequest) returns (stream EventResponse) { - option (google.api.http) = { - post: "/v1/event_stream", - additional_bindings { - get: "/v1/event_stream" - }, - body: "*" - }; - } - - // Integrate with and acquire control of Vector's AI system. - rpc BehaviorControl(stream BehaviorControlRequest) returns (stream BehaviorControlResponse) { - } - - // Acquire control of Vector's AI system. - rpc AssumeBehaviorControl(BehaviorControlRequest) returns (stream BehaviorControlResponse) { - option (google.api.http) = { - post: "/v1/assume_behavior_control", - body: "*" - }; - } - - rpc CancelFaceEnrollment(CancelFaceEnrollmentRequest) returns (CancelFaceEnrollmentResponse) { - option (google.api.http) = { - post: "/v1/cancel_face_enrollment", - body: "*" - }; - } - - // Get a list of names and their IDs. - rpc RequestEnrolledNames(RequestEnrolledNamesRequest) returns (RequestEnrolledNamesResponse) { - option (google.api.http) = { - post: "/v1/request_enrolled_names", - body: "*" - }; - } - - // Update the name enrolled for a given face. - rpc UpdateEnrolledFaceByID(UpdateEnrolledFaceByIDRequest) returns (UpdateEnrolledFaceByIDResponse) { - option (google.api.http) = { - post: "/v1/update_enrolled_face_by_id", - body: "*" - }; - } - - // Erase the enrollment (name) record for the face with this ID. - rpc EraseEnrolledFaceByID(EraseEnrolledFaceByIDRequest) returns (EraseEnrolledFaceByIDResponse) { - option (google.api.http) = { - post: "/v1/erase_enrolled_face_by_id", - body: "*" - }; - } - - // Erase the enrollment (name) records for all faces. - rpc EraseAllEnrolledFaces(EraseAllEnrolledFacesRequest) returns (EraseAllEnrolledFacesResponse) { - option (google.api.http) = { - post: "/v1/erase_all_enrolled_faces", - body: "*" - }; - } - - rpc SetFaceToEnroll(SetFaceToEnrollRequest) returns (SetFaceToEnrollResponse) { - option (google.api.http) = { - post: "/v1/set_face_to_enroll", - body: "*" - }; - } - - rpc EnableMarkerDetection(EnableMarkerDetectionRequest) returns (EnableMarkerDetectionResponse) { - option (google.api.http) = { - post: "/v1/enable_marker_detection", - body: "*" - }; - } - - rpc EnableFaceDetection(EnableFaceDetectionRequest) returns (EnableFaceDetectionResponse) { - option (google.api.http) = { - post: "/v1/enable_face_detection", - body: "*" - }; - } - - rpc EnableMotionDetection(EnableMotionDetectionRequest) returns (EnableMotionDetectionResponse) { - option (google.api.http) = { - post: "/v1/enable_motion_detection", - body: "*" - }; - } - - rpc EnableMirrorMode(EnableMirrorModeRequest) returns (EnableMirrorModeResponse) { - option (google.api.http) = { - post: "/v1/enable_mirror_mode", - body: "*" - }; - } - - rpc EnableImageStreaming(EnableImageStreamingRequest) returns (EnableImageStreamingResponse) { - option (google.api.http) = { - post: "/v1/enable_image_streaming", - body: "*" - }; - } - - // Tells Vector to drive to the specified pose and orientation. - rpc GoToPose(GoToPoseRequest) returns (GoToPoseResponse) { - option (google.api.http) = { - post: "/v1/go_to_pose", - body: "*" - }; - } - - // Tells Vector to dock with a light cube with a given approach angle and distance. - rpc DockWithCube(DockWithCubeRequest) returns (DockWithCubeResponse) { - option (google.api.http) = { - post: "/v1/dock_with_cube", - body: "*" - }; - } - - // Drive Vector off the charger. - rpc DriveOffCharger(DriveOffChargerRequest) returns (DriveOffChargerResponse) { - option (google.api.http) = { - post: "/v1/drive_off_charger", - body: "*" - }; - } - - // Drive Vector onto the charger. - rpc DriveOnCharger(DriveOnChargerRequest) returns (DriveOnChargerResponse) { - option (google.api.http) = { - post: "/v1/drive_on_charger", - body: "*" - }; - } - - // Get the photos info. - rpc PhotosInfo(PhotosInfoRequest) returns (PhotosInfoResponse) { - option (google.api.http) = { - post: "/v1/photos_info", - body: "*" - }; - } - - // Get a photo by ID. - rpc Photo(PhotoRequest) returns (PhotoResponse) { - option (google.api.http) = { - post: "/v1/photo", - body: "*" - }; - } - - // Get a thumbnail by ID. - rpc Thumbnail(ThumbnailRequest) returns (ThumbnailResponse) { - option (google.api.http) = { - post: "/v1/thumbnail", - body: "*" - }; - } - - // Delete a photo by ID. - rpc DeletePhoto(DeletePhotoRequest) returns (DeletePhotoResponse) { - option (google.api.http) = { - post: "/v1/delete_photo", - body: "*" - }; - } - - // Tells Vector to drive in a straight line. - rpc DriveStraight(DriveStraightRequest) returns (DriveStraightResponse) {} - - // Turn the robot around its current position. - rpc TurnInPlace(TurnInPlaceRequest) returns (TurnInPlaceResponse) {} - - // Tell Vector's head to move to a given angle. - rpc SetHeadAngle(SetHeadAngleRequest) returns (SetHeadAngleResponse) {} - - // Tell Vector's lift to move to a given height. - rpc SetLiftHeight(SetLiftHeightRequest) returns (SetLiftHeightResponse) {} - - rpc UserAuthentication(UserAuthenticationRequest) returns (UserAuthenticationResponse) { - option (google.api.http) = { - post: "/v1/user_authentication", - body: "*" - }; - } - - // Check the current state of the battery. - rpc BatteryState(BatteryStateRequest) returns (BatteryStateResponse) { - option (google.api.http) = { - post: "/v1/battery_state", - body: "*" - }; - } - - // Get the versioning information for Vector. - rpc VersionState(VersionStateRequest) returns (VersionStateResponse) { - option (google.api.http) = { - post: "/v1/version_state", - body: "*" - }; - } - - // Get the network information for Vector. - rpc NetworkState(NetworkStateRequest) returns (NetworkStateResponse) { - option (google.api.http) = { - post: "/v1/network_state", - body: "*" - }; - } - - // Make Vector speak text. - rpc SayText(SayTextRequest) returns (SayTextResponse) { - option (google.api.http) = { - post: "/v1/say_text", - body: "*" - }; - } - - // Attempt to connect to a cube. If a cube is currently connected, - // this will do nothing. - rpc ConnectCube(ConnectCubeRequest) returns (ConnectCubeResponse) { - option (google.api.http) = { - post: "/v1/connect_cube", - body: "*" - }; - } - - // Requests a disconnection from the currently connected cube. - rpc DisconnectCube(DisconnectCubeRequest) returns (DisconnectCubeResponse) { - option (google.api.http) = { - post: "/v1/disconnect_cube", - body: "*" - }; - } - - rpc CubesAvailable(CubesAvailableRequest) returns (CubesAvailableResponse) { - option (google.api.http) = { - post: "/v1/cubes_available", - body: "*" - }; - } - - // Signal a connected cube to flash its lights using the default cube - // flash animation. - rpc FlashCubeLights(FlashCubeLightsRequest) returns (FlashCubeLightsResponse) { - option (google.api.http) = { - post: "/v1/flash_cube_lights", - body: "*" - }; - } - - // Forget the robot's preferred cube. This will cause the robot to - // connect to the cube with the highest RSSI (signal strength) next - // time a connection is requested. Saves this preference to disk. - // The next cube that the robot connects to will become its - // preferred cube. - rpc ForgetPreferredCube(ForgetPreferredCubeRequest) returns (ForgetPreferredCubeResponse) { - option (google.api.http) = { - post: "/v1/forget_preferred_cube", - body: "*" - }; - } - - // Set the robot's preferred cube and save it to disk. The robot - // will always attempt to connect to this cube if it is available. - // This is only used in simulation for now. - rpc SetPreferredCube(SetPreferredCubeRequest) returns (SetPreferredCubeResponse) { - option (google.api.http) = { - post: "/v1/set_preferred_cube", - body: "*" - }; - } - - // Causes the robot to forget about custom objects it currently knows about. - rpc DeleteCustomObjects(DeleteCustomObjectsRequest) returns (DeleteCustomObjectsResponse) { - option (google.api.http) = { - post: "/v1/delete_custom_objects", - body: "*" - }; - } - - // Creates a permanent custom object instance in the robot's world, with no connection to the vision system. - rpc CreateFixedCustomObject(CreateFixedCustomObjectRequest) returns (CreateFixedCustomObjectResponse) { - option (google.api.http) = { - post: "/v1/create_fixed_custom_object", - body: "*" - }; - } - - // Creates a custom object with distinct custom marker(s). - rpc DefineCustomObject(DefineCustomObjectRequest) returns (DefineCustomObjectResponse) { - option (google.api.http) = { - post: "/v1/define_custom_object", - body: "*" - }; - } - - // Set each of the lights on the currently connected cube based on two - // rgb values each and timing data for how to transition between them. - rpc SetCubeLights(SetCubeLightsRequest) returns (SetCubeLightsResponse) {} - - // Request an audio feed from the robot. - rpc AudioFeed(AudioFeedRequest) returns (stream AudioFeedResponse) { - option (google.api.http) = { - post: "/v1/audio_feed", - body: "*" - }; - } - - // Request a camera feed from the robot. - rpc CameraFeed(CameraFeedRequest) returns (stream CameraFeedResponse) { - option (google.api.http) = { - post: "/v1/camera_feed", - body: "*" - }; - } - - // Set Vector's eye color. - rpc SetEyeColor(SetEyeColorRequest) returns (SetEyeColorResponse) { - option (google.api.http) = { - post: "/v1/set_eye_color", - body: "*" - }; - } - - rpc NavMapFeed(NavMapFeedRequest) returns (stream NavMapFeedResponse) { - option (google.api.http) = { - post: "/v1/nav_map_feed", - body: "*" - }; - } - -} \ No newline at end of file diff --git a/anki_vector/messaging/external_interface_pb2.py b/anki_vector/messaging/external_interface_pb2.py deleted file mode 100644 index 7ceff3b..0000000 --- a/anki_vector/messaging/external_interface_pb2.py +++ /dev/null @@ -1,529 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/external_interface.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() - - -from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -from anki_vector.messaging import behavior_pb2 as anki__vector_dot_messaging_dot_behavior__pb2 -from anki_vector.messaging import cube_pb2 as anki__vector_dot_messaging_dot_cube__pb2 -from anki_vector.messaging import messages_pb2 as anki__vector_dot_messaging_dot_messages__pb2 -from anki_vector.messaging import nav_map_pb2 as anki__vector_dot_messaging_dot_nav__map__pb2 -from anki_vector.messaging import shared_pb2 as anki__vector_dot_messaging_dot_shared__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='anki_vector/messaging/external_interface.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - serialized_pb=_b('\n.anki_vector/messaging/external_interface.proto\x12\x1e\x41nki.Vector.external_interface\x1a\x1cgoogle/api/annotations.proto\x1a$anki_vector/messaging/behavior.proto\x1a anki_vector/messaging/cube.proto\x1a$anki_vector/messaging/messages.proto\x1a#anki_vector/messaging/nav_map.proto\x1a\"anki_vector/messaging/shared.proto2\xff\x41\n\x11\x45xternalInterface\x12\xa3\x01\n\x0fProtocolVersion\x12\x36.Anki.Vector.external_interface.ProtocolVersionRequest\x1a\x37.Anki.Vector.external_interface.ProtocolVersionResponse\"\x1f\x82\xd3\xe4\x93\x02\x19\"\x14/v1/protocol_version:\x01*\x12\xab\x01\n\x11SDKInitialization\x12\x38.Anki.Vector.external_interface.SDKInitializationRequest\x1a\x39.Anki.Vector.external_interface.SDKInitializationResponse\"!\x82\xd3\xe4\x93\x02\x1b\"\x16/v1/sdk_initialization:\x01*\x12x\n\x0b\x44riveWheels\x12\x32.Anki.Vector.external_interface.DriveWheelsRequest\x1a\x33.Anki.Vector.external_interface.DriveWheelsResponse\"\x00\x12~\n\rPlayAnimation\x12\x34.Anki.Vector.external_interface.PlayAnimationRequest\x1a\x35.Anki.Vector.external_interface.PlayAnimationResponse\"\x00\x12\x9f\x01\n\x0eListAnimations\x12\x35.Anki.Vector.external_interface.ListAnimationsRequest\x1a\x36.Anki.Vector.external_interface.ListAnimationsResponse\"\x1e\x82\xd3\xe4\x93\x02\x18\"\x13/v1/list_animations:\x01*\x12o\n\x08MoveHead\x12/.Anki.Vector.external_interface.MoveHeadRequest\x1a\x30.Anki.Vector.external_interface.MoveHeadResponse\"\x00\x12o\n\x08MoveLift\x12/.Anki.Vector.external_interface.MoveLiftRequest\x1a\x30.Anki.Vector.external_interface.MoveLiftResponse\"\x00\x12\xb5\x01\n\x13\x44isplayFaceImageRGB\x12:.Anki.Vector.external_interface.DisplayFaceImageRGBRequest\x1a;.Anki.Vector.external_interface.DisplayFaceImageRGBResponse\"%\x82\xd3\xe4\x93\x02\x1f\"\x1a/v1/display_face_image_rgb:\x01*\x12\x9d\x01\n\x0b\x45ventStream\x12,.Anki.Vector.external_interface.EventRequest\x1a-.Anki.Vector.external_interface.EventResponse\"/\x82\xd3\xe4\x93\x02)\"\x10/v1/event_stream:\x01*Z\x12\x12\x10/v1/event_stream0\x01\x12\x88\x01\n\x0f\x42\x65haviorControl\x12\x36.Anki.Vector.external_interface.BehaviorControlRequest\x1a\x37.Anki.Vector.external_interface.BehaviorControlResponse\"\x00(\x01\x30\x01\x12\xb2\x01\n\x15\x41ssumeBehaviorControl\x12\x36.Anki.Vector.external_interface.BehaviorControlRequest\x1a\x37.Anki.Vector.external_interface.BehaviorControlResponse\"&\x82\xd3\xe4\x93\x02 \"\x1b/v1/assume_behavior_control:\x01*0\x01\x12\xb8\x01\n\x14\x43\x61ncelFaceEnrollment\x12;.Anki.Vector.external_interface.CancelFaceEnrollmentRequest\x1a<.Anki.Vector.external_interface.CancelFaceEnrollmentResponse\"%\x82\xd3\xe4\x93\x02\x1f\"\x1a/v1/cancel_face_enrollment:\x01*\x12\xb8\x01\n\x14RequestEnrolledNames\x12;.Anki.Vector.external_interface.RequestEnrolledNamesRequest\x1a<.Anki.Vector.external_interface.RequestEnrolledNamesResponse\"%\x82\xd3\xe4\x93\x02\x1f\"\x1a/v1/request_enrolled_names:\x01*\x12\xc2\x01\n\x16UpdateEnrolledFaceByID\x12=.Anki.Vector.external_interface.UpdateEnrolledFaceByIDRequest\x1a>.Anki.Vector.external_interface.UpdateEnrolledFaceByIDResponse\")\x82\xd3\xe4\x93\x02#\"\x1e/v1/update_enrolled_face_by_id:\x01*\x12\xbe\x01\n\x15\x45raseEnrolledFaceByID\x12<.Anki.Vector.external_interface.EraseEnrolledFaceByIDRequest\x1a=.Anki.Vector.external_interface.EraseEnrolledFaceByIDResponse\"(\x82\xd3\xe4\x93\x02\"\"\x1d/v1/erase_enrolled_face_by_id:\x01*\x12\xbd\x01\n\x15\x45raseAllEnrolledFaces\x12<.Anki.Vector.external_interface.EraseAllEnrolledFacesRequest\x1a=.Anki.Vector.external_interface.EraseAllEnrolledFacesResponse\"\'\x82\xd3\xe4\x93\x02!\"\x1c/v1/erase_all_enrolled_faces:\x01*\x12\xa5\x01\n\x0fSetFaceToEnroll\x12\x36.Anki.Vector.external_interface.SetFaceToEnrollRequest\x1a\x37.Anki.Vector.external_interface.SetFaceToEnrollResponse\"!\x82\xd3\xe4\x93\x02\x1b\"\x16/v1/set_face_to_enroll:\x01*\x12\xbc\x01\n\x15\x45nableMarkerDetection\x12<.Anki.Vector.external_interface.EnableMarkerDetectionRequest\x1a=.Anki.Vector.external_interface.EnableMarkerDetectionResponse\"&\x82\xd3\xe4\x93\x02 \"\x1b/v1/enable_marker_detection:\x01*\x12\xb4\x01\n\x13\x45nableFaceDetection\x12:.Anki.Vector.external_interface.EnableFaceDetectionRequest\x1a;.Anki.Vector.external_interface.EnableFaceDetectionResponse\"$\x82\xd3\xe4\x93\x02\x1e\"\x19/v1/enable_face_detection:\x01*\x12\xbc\x01\n\x15\x45nableMotionDetection\x12<.Anki.Vector.external_interface.EnableMotionDetectionRequest\x1a=.Anki.Vector.external_interface.EnableMotionDetectionResponse\"&\x82\xd3\xe4\x93\x02 \"\x1b/v1/enable_motion_detection:\x01*\x12\xa8\x01\n\x10\x45nableMirrorMode\x12\x37.Anki.Vector.external_interface.EnableMirrorModeRequest\x1a\x38.Anki.Vector.external_interface.EnableMirrorModeResponse\"!\x82\xd3\xe4\x93\x02\x1b\"\x16/v1/enable_mirror_mode:\x01*\x12\xb8\x01\n\x14\x45nableImageStreaming\x12;.Anki.Vector.external_interface.EnableImageStreamingRequest\x1a<.Anki.Vector.external_interface.EnableImageStreamingResponse\"%\x82\xd3\xe4\x93\x02\x1f\"\x1a/v1/enable_image_streaming:\x01*\x12\x88\x01\n\x08GoToPose\x12/.Anki.Vector.external_interface.GoToPoseRequest\x1a\x30.Anki.Vector.external_interface.GoToPoseResponse\"\x19\x82\xd3\xe4\x93\x02\x13\"\x0e/v1/go_to_pose:\x01*\x12\x98\x01\n\x0c\x44ockWithCube\x12\x33.Anki.Vector.external_interface.DockWithCubeRequest\x1a\x34.Anki.Vector.external_interface.DockWithCubeResponse\"\x1d\x82\xd3\xe4\x93\x02\x17\"\x12/v1/dock_with_cube:\x01*\x12\xa4\x01\n\x0f\x44riveOffCharger\x12\x36.Anki.Vector.external_interface.DriveOffChargerRequest\x1a\x37.Anki.Vector.external_interface.DriveOffChargerResponse\" \x82\xd3\xe4\x93\x02\x1a\"\x15/v1/drive_off_charger:\x01*\x12\xa0\x01\n\x0e\x44riveOnCharger\x12\x35.Anki.Vector.external_interface.DriveOnChargerRequest\x1a\x36.Anki.Vector.external_interface.DriveOnChargerResponse\"\x1f\x82\xd3\xe4\x93\x02\x19\"\x14/v1/drive_on_charger:\x01*\x12\x8f\x01\n\nPhotosInfo\x12\x31.Anki.Vector.external_interface.PhotosInfoRequest\x1a\x32.Anki.Vector.external_interface.PhotosInfoResponse\"\x1a\x82\xd3\xe4\x93\x02\x14\"\x0f/v1/photos_info:\x01*\x12z\n\x05Photo\x12,.Anki.Vector.external_interface.PhotoRequest\x1a-.Anki.Vector.external_interface.PhotoResponse\"\x14\x82\xd3\xe4\x93\x02\x0e\"\t/v1/photo:\x01*\x12\x8a\x01\n\tThumbnail\x12\x30.Anki.Vector.external_interface.ThumbnailRequest\x1a\x31.Anki.Vector.external_interface.ThumbnailResponse\"\x18\x82\xd3\xe4\x93\x02\x12\"\r/v1/thumbnail:\x01*\x12\x93\x01\n\x0b\x44\x65letePhoto\x12\x32.Anki.Vector.external_interface.DeletePhotoRequest\x1a\x33.Anki.Vector.external_interface.DeletePhotoResponse\"\x1b\x82\xd3\xe4\x93\x02\x15\"\x10/v1/delete_photo:\x01*\x12~\n\rDriveStraight\x12\x34.Anki.Vector.external_interface.DriveStraightRequest\x1a\x35.Anki.Vector.external_interface.DriveStraightResponse\"\x00\x12x\n\x0bTurnInPlace\x12\x32.Anki.Vector.external_interface.TurnInPlaceRequest\x1a\x33.Anki.Vector.external_interface.TurnInPlaceResponse\"\x00\x12{\n\x0cSetHeadAngle\x12\x33.Anki.Vector.external_interface.SetHeadAngleRequest\x1a\x34.Anki.Vector.external_interface.SetHeadAngleResponse\"\x00\x12~\n\rSetLiftHeight\x12\x34.Anki.Vector.external_interface.SetLiftHeightRequest\x1a\x35.Anki.Vector.external_interface.SetLiftHeightResponse\"\x00\x12\xaf\x01\n\x12UserAuthentication\x12\x39.Anki.Vector.external_interface.UserAuthenticationRequest\x1a:.Anki.Vector.external_interface.UserAuthenticationResponse\"\"\x82\xd3\xe4\x93\x02\x1c\"\x17/v1/user_authentication:\x01*\x12\x97\x01\n\x0c\x42\x61tteryState\x12\x33.Anki.Vector.external_interface.BatteryStateRequest\x1a\x34.Anki.Vector.external_interface.BatteryStateResponse\"\x1c\x82\xd3\xe4\x93\x02\x16\"\x11/v1/battery_state:\x01*\x12\x97\x01\n\x0cVersionState\x12\x33.Anki.Vector.external_interface.VersionStateRequest\x1a\x34.Anki.Vector.external_interface.VersionStateResponse\"\x1c\x82\xd3\xe4\x93\x02\x16\"\x11/v1/version_state:\x01*\x12\x97\x01\n\x0cNetworkState\x12\x33.Anki.Vector.external_interface.NetworkStateRequest\x1a\x34.Anki.Vector.external_interface.NetworkStateResponse\"\x1c\x82\xd3\xe4\x93\x02\x16\"\x11/v1/network_state:\x01*\x12\x83\x01\n\x07SayText\x12..Anki.Vector.external_interface.SayTextRequest\x1a/.Anki.Vector.external_interface.SayTextResponse\"\x17\x82\xd3\xe4\x93\x02\x11\"\x0c/v1/say_text:\x01*\x12\x93\x01\n\x0b\x43onnectCube\x12\x32.Anki.Vector.external_interface.ConnectCubeRequest\x1a\x33.Anki.Vector.external_interface.ConnectCubeResponse\"\x1b\x82\xd3\xe4\x93\x02\x15\"\x10/v1/connect_cube:\x01*\x12\x9f\x01\n\x0e\x44isconnectCube\x12\x35.Anki.Vector.external_interface.DisconnectCubeRequest\x1a\x36.Anki.Vector.external_interface.DisconnectCubeResponse\"\x1e\x82\xd3\xe4\x93\x02\x18\"\x13/v1/disconnect_cube:\x01*\x12\x9f\x01\n\x0e\x43ubesAvailable\x12\x35.Anki.Vector.external_interface.CubesAvailableRequest\x1a\x36.Anki.Vector.external_interface.CubesAvailableResponse\"\x1e\x82\xd3\xe4\x93\x02\x18\"\x13/v1/cubes_available:\x01*\x12\xa4\x01\n\x0f\x46lashCubeLights\x12\x36.Anki.Vector.external_interface.FlashCubeLightsRequest\x1a\x37.Anki.Vector.external_interface.FlashCubeLightsResponse\" \x82\xd3\xe4\x93\x02\x1a\"\x15/v1/flash_cube_lights:\x01*\x12\xb4\x01\n\x13\x46orgetPreferredCube\x12:.Anki.Vector.external_interface.ForgetPreferredCubeRequest\x1a;.Anki.Vector.external_interface.ForgetPreferredCubeResponse\"$\x82\xd3\xe4\x93\x02\x1e\"\x19/v1/forget_preferred_cube:\x01*\x12\xa8\x01\n\x10SetPreferredCube\x12\x37.Anki.Vector.external_interface.SetPreferredCubeRequest\x1a\x38.Anki.Vector.external_interface.SetPreferredCubeResponse\"!\x82\xd3\xe4\x93\x02\x1b\"\x16/v1/set_preferred_cube:\x01*\x12\xb4\x01\n\x13\x44\x65leteCustomObjects\x12:.Anki.Vector.external_interface.DeleteCustomObjectsRequest\x1a;.Anki.Vector.external_interface.DeleteCustomObjectsResponse\"$\x82\xd3\xe4\x93\x02\x1e\"\x19/v1/delete_custom_objects:\x01*\x12\xc5\x01\n\x17\x43reateFixedCustomObject\x12>.Anki.Vector.external_interface.CreateFixedCustomObjectRequest\x1a?.Anki.Vector.external_interface.CreateFixedCustomObjectResponse\")\x82\xd3\xe4\x93\x02#\"\x1e/v1/create_fixed_custom_object:\x01*\x12\xb0\x01\n\x12\x44\x65\x66ineCustomObject\x12\x39.Anki.Vector.external_interface.DefineCustomObjectRequest\x1a:.Anki.Vector.external_interface.DefineCustomObjectResponse\"#\x82\xd3\xe4\x93\x02\x1d\"\x18/v1/define_custom_object:\x01*\x12~\n\rSetCubeLights\x12\x34.Anki.Vector.external_interface.SetCubeLightsRequest\x1a\x35.Anki.Vector.external_interface.SetCubeLightsResponse\"\x00\x12\x8d\x01\n\tAudioFeed\x12\x30.Anki.Vector.external_interface.AudioFeedRequest\x1a\x31.Anki.Vector.external_interface.AudioFeedResponse\"\x19\x82\xd3\xe4\x93\x02\x13\"\x0e/v1/audio_feed:\x01*0\x01\x12\x91\x01\n\nCameraFeed\x12\x31.Anki.Vector.external_interface.CameraFeedRequest\x1a\x32.Anki.Vector.external_interface.CameraFeedResponse\"\x1a\x82\xd3\xe4\x93\x02\x14\"\x0f/v1/camera_feed:\x01*0\x01\x12\x94\x01\n\x0bSetEyeColor\x12\x32.Anki.Vector.external_interface.SetEyeColorRequest\x1a\x33.Anki.Vector.external_interface.SetEyeColorResponse\"\x1c\x82\xd3\xe4\x93\x02\x16\"\x11/v1/set_eye_color:\x01*\x12\x92\x01\n\nNavMapFeed\x12\x31.Anki.Vector.external_interface.NavMapFeedRequest\x1a\x32.Anki.Vector.external_interface.NavMapFeedResponse\"\x1b\x82\xd3\xe4\x93\x02\x15\"\x10/v1/nav_map_feed:\x01*0\x01\x62\x06proto3') - , - dependencies=[google_dot_api_dot_annotations__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_behavior__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_cube__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_messages__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_nav__map__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_shared__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - -_EXTERNALINTERFACE = _descriptor.ServiceDescriptor( - name='ExternalInterface', - full_name='Anki.Vector.external_interface.ExternalInterface', - file=DESCRIPTOR, - index=0, - options=None, - serialized_start=296, - serialized_end=8743, - methods=[ - _descriptor.MethodDescriptor( - name='ProtocolVersion', - full_name='Anki.Vector.external_interface.ExternalInterface.ProtocolVersion', - index=0, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_shared__pb2._PROTOCOLVERSIONREQUEST, - output_type=anki__vector_dot_messaging_dot_shared__pb2._PROTOCOLVERSIONRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\031\"\024/v1/protocol_version:\001*')), - ), - _descriptor.MethodDescriptor( - name='SDKInitialization', - full_name='Anki.Vector.external_interface.ExternalInterface.SDKInitialization', - index=1, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._SDKINITIALIZATIONREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._SDKINITIALIZATIONRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\033\"\026/v1/sdk_initialization:\001*')), - ), - _descriptor.MethodDescriptor( - name='DriveWheels', - full_name='Anki.Vector.external_interface.ExternalInterface.DriveWheels', - index=2, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._DRIVEWHEELSREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._DRIVEWHEELSRESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='PlayAnimation', - full_name='Anki.Vector.external_interface.ExternalInterface.PlayAnimation', - index=3, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._PLAYANIMATIONREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._PLAYANIMATIONRESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='ListAnimations', - full_name='Anki.Vector.external_interface.ExternalInterface.ListAnimations', - index=4, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._LISTANIMATIONSREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._LISTANIMATIONSRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\030\"\023/v1/list_animations:\001*')), - ), - _descriptor.MethodDescriptor( - name='MoveHead', - full_name='Anki.Vector.external_interface.ExternalInterface.MoveHead', - index=5, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._MOVEHEADREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._MOVEHEADRESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='MoveLift', - full_name='Anki.Vector.external_interface.ExternalInterface.MoveLift', - index=6, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._MOVELIFTREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._MOVELIFTRESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='DisplayFaceImageRGB', - full_name='Anki.Vector.external_interface.ExternalInterface.DisplayFaceImageRGB', - index=7, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._DISPLAYFACEIMAGERGBREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._DISPLAYFACEIMAGERGBRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\037\"\032/v1/display_face_image_rgb:\001*')), - ), - _descriptor.MethodDescriptor( - name='EventStream', - full_name='Anki.Vector.external_interface.ExternalInterface.EventStream', - index=8, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_shared__pb2._EVENTREQUEST, - output_type=anki__vector_dot_messaging_dot_shared__pb2._EVENTRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002)\"\020/v1/event_stream:\001*Z\022\022\020/v1/event_stream')), - ), - _descriptor.MethodDescriptor( - name='BehaviorControl', - full_name='Anki.Vector.external_interface.ExternalInterface.BehaviorControl', - index=9, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_behavior__pb2._BEHAVIORCONTROLREQUEST, - output_type=anki__vector_dot_messaging_dot_behavior__pb2._BEHAVIORCONTROLRESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='AssumeBehaviorControl', - full_name='Anki.Vector.external_interface.ExternalInterface.AssumeBehaviorControl', - index=10, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_behavior__pb2._BEHAVIORCONTROLREQUEST, - output_type=anki__vector_dot_messaging_dot_behavior__pb2._BEHAVIORCONTROLRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002 \"\033/v1/assume_behavior_control:\001*')), - ), - _descriptor.MethodDescriptor( - name='CancelFaceEnrollment', - full_name='Anki.Vector.external_interface.ExternalInterface.CancelFaceEnrollment', - index=11, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._CANCELFACEENROLLMENTREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._CANCELFACEENROLLMENTRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\037\"\032/v1/cancel_face_enrollment:\001*')), - ), - _descriptor.MethodDescriptor( - name='RequestEnrolledNames', - full_name='Anki.Vector.external_interface.ExternalInterface.RequestEnrolledNames', - index=12, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._REQUESTENROLLEDNAMESREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._REQUESTENROLLEDNAMESRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\037\"\032/v1/request_enrolled_names:\001*')), - ), - _descriptor.MethodDescriptor( - name='UpdateEnrolledFaceByID', - full_name='Anki.Vector.external_interface.ExternalInterface.UpdateEnrolledFaceByID', - index=13, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._UPDATEENROLLEDFACEBYIDREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._UPDATEENROLLEDFACEBYIDRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002#\"\036/v1/update_enrolled_face_by_id:\001*')), - ), - _descriptor.MethodDescriptor( - name='EraseEnrolledFaceByID', - full_name='Anki.Vector.external_interface.ExternalInterface.EraseEnrolledFaceByID', - index=14, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._ERASEENROLLEDFACEBYIDREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._ERASEENROLLEDFACEBYIDRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\"\"\035/v1/erase_enrolled_face_by_id:\001*')), - ), - _descriptor.MethodDescriptor( - name='EraseAllEnrolledFaces', - full_name='Anki.Vector.external_interface.ExternalInterface.EraseAllEnrolledFaces', - index=15, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._ERASEALLENROLLEDFACESREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._ERASEALLENROLLEDFACESRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002!\"\034/v1/erase_all_enrolled_faces:\001*')), - ), - _descriptor.MethodDescriptor( - name='SetFaceToEnroll', - full_name='Anki.Vector.external_interface.ExternalInterface.SetFaceToEnroll', - index=16, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._SETFACETOENROLLREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._SETFACETOENROLLRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\033\"\026/v1/set_face_to_enroll:\001*')), - ), - _descriptor.MethodDescriptor( - name='EnableMarkerDetection', - full_name='Anki.Vector.external_interface.ExternalInterface.EnableMarkerDetection', - index=17, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEMARKERDETECTIONREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEMARKERDETECTIONRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002 \"\033/v1/enable_marker_detection:\001*')), - ), - _descriptor.MethodDescriptor( - name='EnableFaceDetection', - full_name='Anki.Vector.external_interface.ExternalInterface.EnableFaceDetection', - index=18, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEFACEDETECTIONREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEFACEDETECTIONRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\036\"\031/v1/enable_face_detection:\001*')), - ), - _descriptor.MethodDescriptor( - name='EnableMotionDetection', - full_name='Anki.Vector.external_interface.ExternalInterface.EnableMotionDetection', - index=19, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEMOTIONDETECTIONREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEMOTIONDETECTIONRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002 \"\033/v1/enable_motion_detection:\001*')), - ), - _descriptor.MethodDescriptor( - name='EnableMirrorMode', - full_name='Anki.Vector.external_interface.ExternalInterface.EnableMirrorMode', - index=20, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEMIRRORMODEREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEMIRRORMODERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\033\"\026/v1/enable_mirror_mode:\001*')), - ), - _descriptor.MethodDescriptor( - name='EnableImageStreaming', - full_name='Anki.Vector.external_interface.ExternalInterface.EnableImageStreaming', - index=21, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEIMAGESTREAMINGREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._ENABLEIMAGESTREAMINGRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\037\"\032/v1/enable_image_streaming:\001*')), - ), - _descriptor.MethodDescriptor( - name='GoToPose', - full_name='Anki.Vector.external_interface.ExternalInterface.GoToPose', - index=22, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._GOTOPOSEREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._GOTOPOSERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\023\"\016/v1/go_to_pose:\001*')), - ), - _descriptor.MethodDescriptor( - name='DockWithCube', - full_name='Anki.Vector.external_interface.ExternalInterface.DockWithCube', - index=23, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._DOCKWITHCUBEREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._DOCKWITHCUBERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\027\"\022/v1/dock_with_cube:\001*')), - ), - _descriptor.MethodDescriptor( - name='DriveOffCharger', - full_name='Anki.Vector.external_interface.ExternalInterface.DriveOffCharger', - index=24, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._DRIVEOFFCHARGERREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._DRIVEOFFCHARGERRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\032\"\025/v1/drive_off_charger:\001*')), - ), - _descriptor.MethodDescriptor( - name='DriveOnCharger', - full_name='Anki.Vector.external_interface.ExternalInterface.DriveOnCharger', - index=25, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._DRIVEONCHARGERREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._DRIVEONCHARGERRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\031\"\024/v1/drive_on_charger:\001*')), - ), - _descriptor.MethodDescriptor( - name='PhotosInfo', - full_name='Anki.Vector.external_interface.ExternalInterface.PhotosInfo', - index=26, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._PHOTOSINFOREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._PHOTOSINFORESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\024\"\017/v1/photos_info:\001*')), - ), - _descriptor.MethodDescriptor( - name='Photo', - full_name='Anki.Vector.external_interface.ExternalInterface.Photo', - index=27, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._PHOTOREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._PHOTORESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\016\"\t/v1/photo:\001*')), - ), - _descriptor.MethodDescriptor( - name='Thumbnail', - full_name='Anki.Vector.external_interface.ExternalInterface.Thumbnail', - index=28, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._THUMBNAILREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._THUMBNAILRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\022\"\r/v1/thumbnail:\001*')), - ), - _descriptor.MethodDescriptor( - name='DeletePhoto', - full_name='Anki.Vector.external_interface.ExternalInterface.DeletePhoto', - index=29, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._DELETEPHOTOREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._DELETEPHOTORESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\025\"\020/v1/delete_photo:\001*')), - ), - _descriptor.MethodDescriptor( - name='DriveStraight', - full_name='Anki.Vector.external_interface.ExternalInterface.DriveStraight', - index=30, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._DRIVESTRAIGHTREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._DRIVESTRAIGHTRESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='TurnInPlace', - full_name='Anki.Vector.external_interface.ExternalInterface.TurnInPlace', - index=31, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._TURNINPLACEREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._TURNINPLACERESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='SetHeadAngle', - full_name='Anki.Vector.external_interface.ExternalInterface.SetHeadAngle', - index=32, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._SETHEADANGLEREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._SETHEADANGLERESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='SetLiftHeight', - full_name='Anki.Vector.external_interface.ExternalInterface.SetLiftHeight', - index=33, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._SETLIFTHEIGHTREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._SETLIFTHEIGHTRESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='UserAuthentication', - full_name='Anki.Vector.external_interface.ExternalInterface.UserAuthentication', - index=34, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_shared__pb2._USERAUTHENTICATIONREQUEST, - output_type=anki__vector_dot_messaging_dot_shared__pb2._USERAUTHENTICATIONRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\034\"\027/v1/user_authentication:\001*')), - ), - _descriptor.MethodDescriptor( - name='BatteryState', - full_name='Anki.Vector.external_interface.ExternalInterface.BatteryState', - index=35, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._BATTERYSTATEREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._BATTERYSTATERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\026\"\021/v1/battery_state:\001*')), - ), - _descriptor.MethodDescriptor( - name='VersionState', - full_name='Anki.Vector.external_interface.ExternalInterface.VersionState', - index=36, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._VERSIONSTATEREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._VERSIONSTATERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\026\"\021/v1/version_state:\001*')), - ), - _descriptor.MethodDescriptor( - name='NetworkState', - full_name='Anki.Vector.external_interface.ExternalInterface.NetworkState', - index=37, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._NETWORKSTATEREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._NETWORKSTATERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\026\"\021/v1/network_state:\001*')), - ), - _descriptor.MethodDescriptor( - name='SayText', - full_name='Anki.Vector.external_interface.ExternalInterface.SayText', - index=38, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._SAYTEXTREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._SAYTEXTRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\021\"\014/v1/say_text:\001*')), - ), - _descriptor.MethodDescriptor( - name='ConnectCube', - full_name='Anki.Vector.external_interface.ExternalInterface.ConnectCube', - index=39, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._CONNECTCUBEREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._CONNECTCUBERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\025\"\020/v1/connect_cube:\001*')), - ), - _descriptor.MethodDescriptor( - name='DisconnectCube', - full_name='Anki.Vector.external_interface.ExternalInterface.DisconnectCube', - index=40, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._DISCONNECTCUBEREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._DISCONNECTCUBERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\030\"\023/v1/disconnect_cube:\001*')), - ), - _descriptor.MethodDescriptor( - name='CubesAvailable', - full_name='Anki.Vector.external_interface.ExternalInterface.CubesAvailable', - index=41, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._CUBESAVAILABLEREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._CUBESAVAILABLERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\030\"\023/v1/cubes_available:\001*')), - ), - _descriptor.MethodDescriptor( - name='FlashCubeLights', - full_name='Anki.Vector.external_interface.ExternalInterface.FlashCubeLights', - index=42, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._FLASHCUBELIGHTSREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._FLASHCUBELIGHTSRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\032\"\025/v1/flash_cube_lights:\001*')), - ), - _descriptor.MethodDescriptor( - name='ForgetPreferredCube', - full_name='Anki.Vector.external_interface.ExternalInterface.ForgetPreferredCube', - index=43, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._FORGETPREFERREDCUBEREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._FORGETPREFERREDCUBERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\036\"\031/v1/forget_preferred_cube:\001*')), - ), - _descriptor.MethodDescriptor( - name='SetPreferredCube', - full_name='Anki.Vector.external_interface.ExternalInterface.SetPreferredCube', - index=44, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._SETPREFERREDCUBEREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._SETPREFERREDCUBERESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\033\"\026/v1/set_preferred_cube:\001*')), - ), - _descriptor.MethodDescriptor( - name='DeleteCustomObjects', - full_name='Anki.Vector.external_interface.ExternalInterface.DeleteCustomObjects', - index=45, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._DELETECUSTOMOBJECTSREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._DELETECUSTOMOBJECTSRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\036\"\031/v1/delete_custom_objects:\001*')), - ), - _descriptor.MethodDescriptor( - name='CreateFixedCustomObject', - full_name='Anki.Vector.external_interface.ExternalInterface.CreateFixedCustomObject', - index=46, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._CREATEFIXEDCUSTOMOBJECTREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._CREATEFIXEDCUSTOMOBJECTRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002#\"\036/v1/create_fixed_custom_object:\001*')), - ), - _descriptor.MethodDescriptor( - name='DefineCustomObject', - full_name='Anki.Vector.external_interface.ExternalInterface.DefineCustomObject', - index=47, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._DEFINECUSTOMOBJECTREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._DEFINECUSTOMOBJECTRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\035\"\030/v1/define_custom_object:\001*')), - ), - _descriptor.MethodDescriptor( - name='SetCubeLights', - full_name='Anki.Vector.external_interface.ExternalInterface.SetCubeLights', - index=48, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_cube__pb2._SETCUBELIGHTSREQUEST, - output_type=anki__vector_dot_messaging_dot_cube__pb2._SETCUBELIGHTSRESPONSE, - options=None, - ), - _descriptor.MethodDescriptor( - name='AudioFeed', - full_name='Anki.Vector.external_interface.ExternalInterface.AudioFeed', - index=49, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._AUDIOFEEDREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._AUDIOFEEDRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\023\"\016/v1/audio_feed:\001*')), - ), - _descriptor.MethodDescriptor( - name='CameraFeed', - full_name='Anki.Vector.external_interface.ExternalInterface.CameraFeed', - index=50, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._CAMERAFEEDREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._CAMERAFEEDRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\024\"\017/v1/camera_feed:\001*')), - ), - _descriptor.MethodDescriptor( - name='SetEyeColor', - full_name='Anki.Vector.external_interface.ExternalInterface.SetEyeColor', - index=51, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_messages__pb2._SETEYECOLORREQUEST, - output_type=anki__vector_dot_messaging_dot_messages__pb2._SETEYECOLORRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\026\"\021/v1/set_eye_color:\001*')), - ), - _descriptor.MethodDescriptor( - name='NavMapFeed', - full_name='Anki.Vector.external_interface.ExternalInterface.NavMapFeed', - index=52, - containing_service=None, - input_type=anki__vector_dot_messaging_dot_nav__map__pb2._NAVMAPFEEDREQUEST, - output_type=anki__vector_dot_messaging_dot_nav__map__pb2._NAVMAPFEEDRESPONSE, - options=_descriptor._ParseOptions(descriptor_pb2.MethodOptions(), _b('\202\323\344\223\002\025\"\020/v1/nav_map_feed:\001*')), - ), -]) -_sym_db.RegisterServiceDescriptor(_EXTERNALINTERFACE) - -DESCRIPTOR.services_by_name['ExternalInterface'] = _EXTERNALINTERFACE - -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/external_interface_pb2_grpc.py b/anki_vector/messaging/external_interface_pb2_grpc.py deleted file mode 100644 index 6f3ed72..0000000 --- a/anki_vector/messaging/external_interface_pb2_grpc.py +++ /dev/null @@ -1,944 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -from anki_vector.messaging import behavior_pb2 as anki__vector_dot_messaging_dot_behavior__pb2 -from anki_vector.messaging import cube_pb2 as anki__vector_dot_messaging_dot_cube__pb2 -from anki_vector.messaging import messages_pb2 as anki__vector_dot_messaging_dot_messages__pb2 -from anki_vector.messaging import nav_map_pb2 as anki__vector_dot_messaging_dot_nav__map__pb2 -from anki_vector.messaging import shared_pb2 as anki__vector_dot_messaging_dot_shared__pb2 - - -class ExternalInterfaceStub(object): - """The grpc-defined connection between the SDK and Anki's Vector robot. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.ProtocolVersion = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/ProtocolVersion', - request_serializer=anki__vector_dot_messaging_dot_shared__pb2.ProtocolVersionRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_shared__pb2.ProtocolVersionResponse.FromString, - ) - self.SDKInitialization = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/SDKInitialization', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.SDKInitializationRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SDKInitializationResponse.FromString, - ) - self.DriveWheels = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DriveWheels', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.DriveWheelsRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DriveWheelsResponse.FromString, - ) - self.PlayAnimation = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/PlayAnimation', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.PlayAnimationRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.PlayAnimationResponse.FromString, - ) - self.ListAnimations = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/ListAnimations', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.ListAnimationsRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.ListAnimationsResponse.FromString, - ) - self.MoveHead = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/MoveHead', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.MoveHeadRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.MoveHeadResponse.FromString, - ) - self.MoveLift = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/MoveLift', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.MoveLiftRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.MoveLiftResponse.FromString, - ) - self.DisplayFaceImageRGB = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DisplayFaceImageRGB', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.DisplayFaceImageRGBRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DisplayFaceImageRGBResponse.FromString, - ) - self.EventStream = channel.unary_stream( - '/Anki.Vector.external_interface.ExternalInterface/EventStream', - request_serializer=anki__vector_dot_messaging_dot_shared__pb2.EventRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_shared__pb2.EventResponse.FromString, - ) - self.BehaviorControl = channel.stream_stream( - '/Anki.Vector.external_interface.ExternalInterface/BehaviorControl', - request_serializer=anki__vector_dot_messaging_dot_behavior__pb2.BehaviorControlRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_behavior__pb2.BehaviorControlResponse.FromString, - ) - self.AssumeBehaviorControl = channel.unary_stream( - '/Anki.Vector.external_interface.ExternalInterface/AssumeBehaviorControl', - request_serializer=anki__vector_dot_messaging_dot_behavior__pb2.BehaviorControlRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_behavior__pb2.BehaviorControlResponse.FromString, - ) - self.CancelFaceEnrollment = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/CancelFaceEnrollment', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.CancelFaceEnrollmentRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.CancelFaceEnrollmentResponse.FromString, - ) - self.RequestEnrolledNames = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/RequestEnrolledNames', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.RequestEnrolledNamesRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.RequestEnrolledNamesResponse.FromString, - ) - self.UpdateEnrolledFaceByID = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/UpdateEnrolledFaceByID', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.UpdateEnrolledFaceByIDRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.UpdateEnrolledFaceByIDResponse.FromString, - ) - self.EraseEnrolledFaceByID = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/EraseEnrolledFaceByID', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.EraseEnrolledFaceByIDRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EraseEnrolledFaceByIDResponse.FromString, - ) - self.EraseAllEnrolledFaces = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/EraseAllEnrolledFaces', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.EraseAllEnrolledFacesRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EraseAllEnrolledFacesResponse.FromString, - ) - self.SetFaceToEnroll = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/SetFaceToEnroll', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.SetFaceToEnrollRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SetFaceToEnrollResponse.FromString, - ) - self.EnableMarkerDetection = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/EnableMarkerDetection', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMarkerDetectionRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMarkerDetectionResponse.FromString, - ) - self.EnableFaceDetection = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/EnableFaceDetection', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableFaceDetectionRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableFaceDetectionResponse.FromString, - ) - self.EnableMotionDetection = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/EnableMotionDetection', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMotionDetectionRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMotionDetectionResponse.FromString, - ) - self.EnableMirrorMode = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/EnableMirrorMode', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMirrorModeRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMirrorModeResponse.FromString, - ) - self.EnableImageStreaming = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/EnableImageStreaming', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableImageStreamingRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableImageStreamingResponse.FromString, - ) - self.GoToPose = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/GoToPose', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.GoToPoseRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.GoToPoseResponse.FromString, - ) - self.DockWithCube = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DockWithCube', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.DockWithCubeRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DockWithCubeResponse.FromString, - ) - self.DriveOffCharger = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DriveOffCharger', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.DriveOffChargerRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DriveOffChargerResponse.FromString, - ) - self.DriveOnCharger = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DriveOnCharger', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.DriveOnChargerRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DriveOnChargerResponse.FromString, - ) - self.PhotosInfo = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/PhotosInfo', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.PhotosInfoRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.PhotosInfoResponse.FromString, - ) - self.Photo = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/Photo', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.PhotoRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.PhotoResponse.FromString, - ) - self.Thumbnail = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/Thumbnail', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.ThumbnailRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.ThumbnailResponse.FromString, - ) - self.DeletePhoto = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DeletePhoto', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.DeletePhotoRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DeletePhotoResponse.FromString, - ) - self.DriveStraight = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DriveStraight', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.DriveStraightRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DriveStraightResponse.FromString, - ) - self.TurnInPlace = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/TurnInPlace', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.TurnInPlaceRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.TurnInPlaceResponse.FromString, - ) - self.SetHeadAngle = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/SetHeadAngle', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.SetHeadAngleRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SetHeadAngleResponse.FromString, - ) - self.SetLiftHeight = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/SetLiftHeight', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.SetLiftHeightRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SetLiftHeightResponse.FromString, - ) - self.UserAuthentication = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/UserAuthentication', - request_serializer=anki__vector_dot_messaging_dot_shared__pb2.UserAuthenticationRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_shared__pb2.UserAuthenticationResponse.FromString, - ) - self.BatteryState = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/BatteryState', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.BatteryStateRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.BatteryStateResponse.FromString, - ) - self.VersionState = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/VersionState', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.VersionStateRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.VersionStateResponse.FromString, - ) - self.NetworkState = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/NetworkState', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.NetworkStateRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.NetworkStateResponse.FromString, - ) - self.SayText = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/SayText', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.SayTextRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SayTextResponse.FromString, - ) - self.ConnectCube = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/ConnectCube', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.ConnectCubeRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.ConnectCubeResponse.FromString, - ) - self.DisconnectCube = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DisconnectCube', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.DisconnectCubeRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.DisconnectCubeResponse.FromString, - ) - self.CubesAvailable = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/CubesAvailable', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.CubesAvailableRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.CubesAvailableResponse.FromString, - ) - self.FlashCubeLights = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/FlashCubeLights', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.FlashCubeLightsRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.FlashCubeLightsResponse.FromString, - ) - self.ForgetPreferredCube = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/ForgetPreferredCube', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.ForgetPreferredCubeRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.ForgetPreferredCubeResponse.FromString, - ) - self.SetPreferredCube = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/SetPreferredCube', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.SetPreferredCubeRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.SetPreferredCubeResponse.FromString, - ) - self.DeleteCustomObjects = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DeleteCustomObjects', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.DeleteCustomObjectsRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.DeleteCustomObjectsResponse.FromString, - ) - self.CreateFixedCustomObject = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/CreateFixedCustomObject', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.CreateFixedCustomObjectRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.CreateFixedCustomObjectResponse.FromString, - ) - self.DefineCustomObject = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/DefineCustomObject', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.DefineCustomObjectRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.DefineCustomObjectResponse.FromString, - ) - self.SetCubeLights = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/SetCubeLights', - request_serializer=anki__vector_dot_messaging_dot_cube__pb2.SetCubeLightsRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_cube__pb2.SetCubeLightsResponse.FromString, - ) - self.AudioFeed = channel.unary_stream( - '/Anki.Vector.external_interface.ExternalInterface/AudioFeed', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.AudioFeedRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.AudioFeedResponse.FromString, - ) - self.CameraFeed = channel.unary_stream( - '/Anki.Vector.external_interface.ExternalInterface/CameraFeed', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.CameraFeedRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.CameraFeedResponse.FromString, - ) - self.SetEyeColor = channel.unary_unary( - '/Anki.Vector.external_interface.ExternalInterface/SetEyeColor', - request_serializer=anki__vector_dot_messaging_dot_messages__pb2.SetEyeColorRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SetEyeColorResponse.FromString, - ) - self.NavMapFeed = channel.unary_stream( - '/Anki.Vector.external_interface.ExternalInterface/NavMapFeed', - request_serializer=anki__vector_dot_messaging_dot_nav__map__pb2.NavMapFeedRequest.SerializeToString, - response_deserializer=anki__vector_dot_messaging_dot_nav__map__pb2.NavMapFeedResponse.FromString, - ) - - -class ExternalInterfaceServicer(object): - """The grpc-defined connection between the SDK and Anki's Vector robot. - """ - - def ProtocolVersion(self, request, context): - """Checks the supported protocol version by passing in the client version and - minimum host version and receiving a response to see whether the versions are supported. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SDKInitialization(self, request, context): - """SDK-only message to pass version info for device OS, Python version, etc. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DriveWheels(self, request, context): - """Sets the speed and acceleration for Vector's wheel motors. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def PlayAnimation(self, request, context): - """Requests that Vector play an animation. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ListAnimations(self, request, context): - """Constructs and returns a list of animations. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def MoveHead(self, request, context): - """Moves Vector's head. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def MoveLift(self, request, context): - """Moves Vector's lift. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DisplayFaceImageRGB(self, request, context): - """Sets screen (Vector's face) to a solid color. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def EventStream(self, request, context): - """Streaming events endpoint - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def BehaviorControl(self, request_iterator, context): - """Integrate with and acquire control of Vector's AI system. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def AssumeBehaviorControl(self, request, context): - """Acquire control of Vector's AI system. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def CancelFaceEnrollment(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def RequestEnrolledNames(self, request, context): - """Get a list of names and their IDs. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def UpdateEnrolledFaceByID(self, request, context): - """Update the name enrolled for a given face. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def EraseEnrolledFaceByID(self, request, context): - """Erase the enrollment (name) record for the face with this ID. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def EraseAllEnrolledFaces(self, request, context): - """Erase the enrollment (name) records for all faces. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetFaceToEnroll(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def EnableMarkerDetection(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def EnableFaceDetection(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def EnableMotionDetection(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def EnableMirrorMode(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def EnableImageStreaming(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def GoToPose(self, request, context): - """Tells Vector to drive to the specified pose and orientation. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DockWithCube(self, request, context): - """Tells Vector to dock with a light cube with a given approach angle and distance. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DriveOffCharger(self, request, context): - """Drive Vector off the charger. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DriveOnCharger(self, request, context): - """Drive Vector onto the charger. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def PhotosInfo(self, request, context): - """Get the photos info. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Photo(self, request, context): - """Get a photo by ID. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Thumbnail(self, request, context): - """Get a thumbnail by ID. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DeletePhoto(self, request, context): - """Delete a photo by ID. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DriveStraight(self, request, context): - """Tells Vector to drive in a straight line. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def TurnInPlace(self, request, context): - """Turn the robot around its current position. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetHeadAngle(self, request, context): - """Tell Vector's head to move to a given angle. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetLiftHeight(self, request, context): - """Tell Vector's lift to move to a given height. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def UserAuthentication(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def BatteryState(self, request, context): - """Check the current state of the battery. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def VersionState(self, request, context): - """Get the versioning information for Vector. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def NetworkState(self, request, context): - """Get the network information for Vector. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SayText(self, request, context): - """Make Vector speak text. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ConnectCube(self, request, context): - """Attempt to connect to a cube. If a cube is currently connected, - this will do nothing. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DisconnectCube(self, request, context): - """Requests a disconnection from the currently connected cube. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def CubesAvailable(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def FlashCubeLights(self, request, context): - """Signal a connected cube to flash its lights using the default cube - flash animation. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ForgetPreferredCube(self, request, context): - """Forget the robot's preferred cube. This will cause the robot to - connect to the cube with the highest RSSI (signal strength) next - time a connection is requested. Saves this preference to disk. - The next cube that the robot connects to will become its - preferred cube. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetPreferredCube(self, request, context): - """Set the robot's preferred cube and save it to disk. The robot - will always attempt to connect to this cube if it is available. - This is only used in simulation for now. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DeleteCustomObjects(self, request, context): - """Causes the robot to forget about custom objects it currently knows about. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def CreateFixedCustomObject(self, request, context): - """Creates a permanent custom object instance in the robot's world, with no connection to the vision system. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DefineCustomObject(self, request, context): - """Creates a custom object with distinct custom marker(s). - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetCubeLights(self, request, context): - """Set each of the lights on the currently connected cube based on two - rgb values each and timing data for how to transition between them. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def AudioFeed(self, request, context): - """Request an audio feed from the robot. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def CameraFeed(self, request, context): - """Request a camera feed from the robot. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetEyeColor(self, request, context): - """Set Vector's eye color. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def NavMapFeed(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_ExternalInterfaceServicer_to_server(servicer, server): - rpc_method_handlers = { - 'ProtocolVersion': grpc.unary_unary_rpc_method_handler( - servicer.ProtocolVersion, - request_deserializer=anki__vector_dot_messaging_dot_shared__pb2.ProtocolVersionRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_shared__pb2.ProtocolVersionResponse.SerializeToString, - ), - 'SDKInitialization': grpc.unary_unary_rpc_method_handler( - servicer.SDKInitialization, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SDKInitializationRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.SDKInitializationResponse.SerializeToString, - ), - 'DriveWheels': grpc.unary_unary_rpc_method_handler( - servicer.DriveWheels, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DriveWheelsRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.DriveWheelsResponse.SerializeToString, - ), - 'PlayAnimation': grpc.unary_unary_rpc_method_handler( - servicer.PlayAnimation, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.PlayAnimationRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.PlayAnimationResponse.SerializeToString, - ), - 'ListAnimations': grpc.unary_unary_rpc_method_handler( - servicer.ListAnimations, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.ListAnimationsRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.ListAnimationsResponse.SerializeToString, - ), - 'MoveHead': grpc.unary_unary_rpc_method_handler( - servicer.MoveHead, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.MoveHeadRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.MoveHeadResponse.SerializeToString, - ), - 'MoveLift': grpc.unary_unary_rpc_method_handler( - servicer.MoveLift, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.MoveLiftRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.MoveLiftResponse.SerializeToString, - ), - 'DisplayFaceImageRGB': grpc.unary_unary_rpc_method_handler( - servicer.DisplayFaceImageRGB, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DisplayFaceImageRGBRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.DisplayFaceImageRGBResponse.SerializeToString, - ), - 'EventStream': grpc.unary_stream_rpc_method_handler( - servicer.EventStream, - request_deserializer=anki__vector_dot_messaging_dot_shared__pb2.EventRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_shared__pb2.EventResponse.SerializeToString, - ), - 'BehaviorControl': grpc.stream_stream_rpc_method_handler( - servicer.BehaviorControl, - request_deserializer=anki__vector_dot_messaging_dot_behavior__pb2.BehaviorControlRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_behavior__pb2.BehaviorControlResponse.SerializeToString, - ), - 'AssumeBehaviorControl': grpc.unary_stream_rpc_method_handler( - servicer.AssumeBehaviorControl, - request_deserializer=anki__vector_dot_messaging_dot_behavior__pb2.BehaviorControlRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_behavior__pb2.BehaviorControlResponse.SerializeToString, - ), - 'CancelFaceEnrollment': grpc.unary_unary_rpc_method_handler( - servicer.CancelFaceEnrollment, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.CancelFaceEnrollmentRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.CancelFaceEnrollmentResponse.SerializeToString, - ), - 'RequestEnrolledNames': grpc.unary_unary_rpc_method_handler( - servicer.RequestEnrolledNames, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.RequestEnrolledNamesRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.RequestEnrolledNamesResponse.SerializeToString, - ), - 'UpdateEnrolledFaceByID': grpc.unary_unary_rpc_method_handler( - servicer.UpdateEnrolledFaceByID, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.UpdateEnrolledFaceByIDRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.UpdateEnrolledFaceByIDResponse.SerializeToString, - ), - 'EraseEnrolledFaceByID': grpc.unary_unary_rpc_method_handler( - servicer.EraseEnrolledFaceByID, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EraseEnrolledFaceByIDRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.EraseEnrolledFaceByIDResponse.SerializeToString, - ), - 'EraseAllEnrolledFaces': grpc.unary_unary_rpc_method_handler( - servicer.EraseAllEnrolledFaces, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EraseAllEnrolledFacesRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.EraseAllEnrolledFacesResponse.SerializeToString, - ), - 'SetFaceToEnroll': grpc.unary_unary_rpc_method_handler( - servicer.SetFaceToEnroll, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SetFaceToEnrollRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.SetFaceToEnrollResponse.SerializeToString, - ), - 'EnableMarkerDetection': grpc.unary_unary_rpc_method_handler( - servicer.EnableMarkerDetection, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMarkerDetectionRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMarkerDetectionResponse.SerializeToString, - ), - 'EnableFaceDetection': grpc.unary_unary_rpc_method_handler( - servicer.EnableFaceDetection, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableFaceDetectionRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableFaceDetectionResponse.SerializeToString, - ), - 'EnableMotionDetection': grpc.unary_unary_rpc_method_handler( - servicer.EnableMotionDetection, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMotionDetectionRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMotionDetectionResponse.SerializeToString, - ), - 'EnableMirrorMode': grpc.unary_unary_rpc_method_handler( - servicer.EnableMirrorMode, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMirrorModeRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableMirrorModeResponse.SerializeToString, - ), - 'EnableImageStreaming': grpc.unary_unary_rpc_method_handler( - servicer.EnableImageStreaming, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.EnableImageStreamingRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.EnableImageStreamingResponse.SerializeToString, - ), - 'GoToPose': grpc.unary_unary_rpc_method_handler( - servicer.GoToPose, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.GoToPoseRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.GoToPoseResponse.SerializeToString, - ), - 'DockWithCube': grpc.unary_unary_rpc_method_handler( - servicer.DockWithCube, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DockWithCubeRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.DockWithCubeResponse.SerializeToString, - ), - 'DriveOffCharger': grpc.unary_unary_rpc_method_handler( - servicer.DriveOffCharger, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DriveOffChargerRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.DriveOffChargerResponse.SerializeToString, - ), - 'DriveOnCharger': grpc.unary_unary_rpc_method_handler( - servicer.DriveOnCharger, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DriveOnChargerRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.DriveOnChargerResponse.SerializeToString, - ), - 'PhotosInfo': grpc.unary_unary_rpc_method_handler( - servicer.PhotosInfo, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.PhotosInfoRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.PhotosInfoResponse.SerializeToString, - ), - 'Photo': grpc.unary_unary_rpc_method_handler( - servicer.Photo, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.PhotoRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.PhotoResponse.SerializeToString, - ), - 'Thumbnail': grpc.unary_unary_rpc_method_handler( - servicer.Thumbnail, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.ThumbnailRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.ThumbnailResponse.SerializeToString, - ), - 'DeletePhoto': grpc.unary_unary_rpc_method_handler( - servicer.DeletePhoto, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DeletePhotoRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.DeletePhotoResponse.SerializeToString, - ), - 'DriveStraight': grpc.unary_unary_rpc_method_handler( - servicer.DriveStraight, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.DriveStraightRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.DriveStraightResponse.SerializeToString, - ), - 'TurnInPlace': grpc.unary_unary_rpc_method_handler( - servicer.TurnInPlace, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.TurnInPlaceRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.TurnInPlaceResponse.SerializeToString, - ), - 'SetHeadAngle': grpc.unary_unary_rpc_method_handler( - servicer.SetHeadAngle, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SetHeadAngleRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.SetHeadAngleResponse.SerializeToString, - ), - 'SetLiftHeight': grpc.unary_unary_rpc_method_handler( - servicer.SetLiftHeight, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SetLiftHeightRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.SetLiftHeightResponse.SerializeToString, - ), - 'UserAuthentication': grpc.unary_unary_rpc_method_handler( - servicer.UserAuthentication, - request_deserializer=anki__vector_dot_messaging_dot_shared__pb2.UserAuthenticationRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_shared__pb2.UserAuthenticationResponse.SerializeToString, - ), - 'BatteryState': grpc.unary_unary_rpc_method_handler( - servicer.BatteryState, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.BatteryStateRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.BatteryStateResponse.SerializeToString, - ), - 'VersionState': grpc.unary_unary_rpc_method_handler( - servicer.VersionState, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.VersionStateRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.VersionStateResponse.SerializeToString, - ), - 'NetworkState': grpc.unary_unary_rpc_method_handler( - servicer.NetworkState, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.NetworkStateRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.NetworkStateResponse.SerializeToString, - ), - 'SayText': grpc.unary_unary_rpc_method_handler( - servicer.SayText, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SayTextRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.SayTextResponse.SerializeToString, - ), - 'ConnectCube': grpc.unary_unary_rpc_method_handler( - servicer.ConnectCube, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.ConnectCubeRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.ConnectCubeResponse.SerializeToString, - ), - 'DisconnectCube': grpc.unary_unary_rpc_method_handler( - servicer.DisconnectCube, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.DisconnectCubeRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.DisconnectCubeResponse.SerializeToString, - ), - 'CubesAvailable': grpc.unary_unary_rpc_method_handler( - servicer.CubesAvailable, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.CubesAvailableRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.CubesAvailableResponse.SerializeToString, - ), - 'FlashCubeLights': grpc.unary_unary_rpc_method_handler( - servicer.FlashCubeLights, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.FlashCubeLightsRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.FlashCubeLightsResponse.SerializeToString, - ), - 'ForgetPreferredCube': grpc.unary_unary_rpc_method_handler( - servicer.ForgetPreferredCube, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.ForgetPreferredCubeRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.ForgetPreferredCubeResponse.SerializeToString, - ), - 'SetPreferredCube': grpc.unary_unary_rpc_method_handler( - servicer.SetPreferredCube, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.SetPreferredCubeRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.SetPreferredCubeResponse.SerializeToString, - ), - 'DeleteCustomObjects': grpc.unary_unary_rpc_method_handler( - servicer.DeleteCustomObjects, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.DeleteCustomObjectsRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.DeleteCustomObjectsResponse.SerializeToString, - ), - 'CreateFixedCustomObject': grpc.unary_unary_rpc_method_handler( - servicer.CreateFixedCustomObject, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.CreateFixedCustomObjectRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.CreateFixedCustomObjectResponse.SerializeToString, - ), - 'DefineCustomObject': grpc.unary_unary_rpc_method_handler( - servicer.DefineCustomObject, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.DefineCustomObjectRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.DefineCustomObjectResponse.SerializeToString, - ), - 'SetCubeLights': grpc.unary_unary_rpc_method_handler( - servicer.SetCubeLights, - request_deserializer=anki__vector_dot_messaging_dot_cube__pb2.SetCubeLightsRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_cube__pb2.SetCubeLightsResponse.SerializeToString, - ), - 'AudioFeed': grpc.unary_stream_rpc_method_handler( - servicer.AudioFeed, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.AudioFeedRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.AudioFeedResponse.SerializeToString, - ), - 'CameraFeed': grpc.unary_stream_rpc_method_handler( - servicer.CameraFeed, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.CameraFeedRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.CameraFeedResponse.SerializeToString, - ), - 'SetEyeColor': grpc.unary_unary_rpc_method_handler( - servicer.SetEyeColor, - request_deserializer=anki__vector_dot_messaging_dot_messages__pb2.SetEyeColorRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_messages__pb2.SetEyeColorResponse.SerializeToString, - ), - 'NavMapFeed': grpc.unary_stream_rpc_method_handler( - servicer.NavMapFeed, - request_deserializer=anki__vector_dot_messaging_dot_nav__map__pb2.NavMapFeedRequest.FromString, - response_serializer=anki__vector_dot_messaging_dot_nav__map__pb2.NavMapFeedResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'Anki.Vector.external_interface.ExternalInterface', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/anki_vector/messaging/messages.proto b/anki_vector/messaging/messages.proto deleted file mode 100644 index 5792cbe..0000000 --- a/anki_vector/messaging/messages.proto +++ /dev/null @@ -1,920 +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. - -// External interface for robot <-> app and robot <-> sdk communication - -syntax = "proto3"; - -package Anki.Vector.external_interface; - -import "anki_vector/messaging/response_status.proto"; -import "anki_vector/messaging/extensions.proto"; - -// A null message used by streams to verify that the client is -// still connected. -message KeepAlivePing {} - -message Animation { - string name = 1; -} - -message DriveWheelsRequest { - float left_wheel_mmps = 1; - float right_wheel_mmps = 2; - float left_wheel_mmps2 = 3; - float right_wheel_mmps2 = 4; -} - -message DriveWheelsResponse { - ResponseStatus status = 1; -} - -message MoveHeadRequest { - float speed_rad_per_sec = 1; -} - -message MoveHeadResponse { - ResponseStatus status = 1; -} - -message MoveLiftRequest { - float speed_rad_per_sec = 1; -} - -message MoveLiftResponse { - ResponseStatus status = 1; -} - -message PlayAnimationRequest { - Animation animation = 1; - uint32 loops = 2; - bool ignore_body_track = 3; - bool ignore_head_track = 4; - bool ignore_lift_track = 5; -} - -message PlayAnimationResponse { - ResponseStatus status = 1; - BehaviorResults result = 2; -} - -message ListAnimationsRequest {} - -message ListAnimationsResponse { - ResponseStatus status = 1; - repeated Animation animation_names = 2; -} - -message DisplayFaceImageRGBRequest { - bytes face_data = 1; - uint32 duration_ms = 2; - bool interrupt_running = 3; -} - -message DisplayFaceImageRGBResponse { - ResponseStatus status = 1; -} - -message MeetVictorFaceScanStarted -{ -} - -message MeetVictorFaceScanComplete -{ -} - -message Status { - oneof status_type { - MeetVictorFaceScanStarted meet_victor_face_scan_started = 2; - MeetVictorFaceScanComplete meet_victor_face_scan_complete = 3; - FaceEnrollmentCompleted face_enrollment_completed = 4; - } -} - -message WakeWord { - oneof wake_word_type { - WakeWordBegin wake_word_begin = 1; - WakeWordEnd wake_word_end = 2; - } -} - -message TimeStampedStatus { - Status status = 1; - uint32 timestamp_utc = 2; -} - -message PoseStruct { - // Translation - float x = 1; - float y = 2; - float z = 3; - - // Rotation quaternion - float q0 = 4; - float q1 = 5; - float q2 = 6; - float q3 = 7; - - uint32 origin_id = 8; // Which coordinate frame this pose is in (0 for none or unknown) -} - -message AccelData { - float x = 1; // mm/s^2 - float y = 2; - float z = 3; -} - -message GyroData { - float x = 1; // rad/s - float y = 2; - float z = 3; -} - -message ProxData { - uint32 distance_mm = 1; - float signal_quality = 2; - bool is_in_valid_range = 3; // Distance is within valid range - bool is_valid_signal_quality = 4; // Signal quality is sufficiently strong to trust that something was detected - bool is_lift_in_fov = 5; // Lift (or object on lift) is occluding the sensor - bool is_too_pitched = 6; // Robot is too far pitched up or down -} - -message TouchData { - uint32 raw_touch_value = 1; // Raw input from the touch sensor - bool is_being_touched = 2; // Robot's context aware evaluation of whether it currently is or isn't being touched -} - -message RobotState { - PoseStruct pose = 1; - float pose_angle_rad = 2; - float pose_pitch_rad = 3; - float left_wheel_speed_mmps = 4; - float right_wheel_speed_mmps = 5; - float head_angle_rad = 6; - float lift_height_mm = 7; - AccelData accel = 8; - GyroData gyro = 9; - int32 carrying_object_id = 10; - int32 carrying_object_on_top_id = 11; // Not supported by engine - int32 head_tracking_object_id = 12; - int32 localized_to_object_id = 13; - uint32 last_image_time_stamp = 14; - uint32 status = 15; - ProxData prox_data = 16; - 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; - float y = 2; -} - -message CladRect -{ - float x_top_left = 1; - float y_top_left = 2; - float width = 3; - float height = 4; -} - -// This is an int8 on the clad side. -// Proto field names are prefixed with "EXPRESSION_" -enum FacialExpression -{ - option allow_alias = true; - EXPRESSION_UNKNOWN = 0; // e.g. expression estimation disabled. - - EXPRESSION_NEUTRAL = 1; - EXPRESSION_HAPPINESS = 2; - EXPRESSION_SURPRISE = 3; - EXPRESSION_ANGER = 4; - EXPRESSION_SADNESS = 5; - - // Clad does not account for "Unknown" in the count. - EXPRESSION_COUNT = 5; -} - -message RobotObservedFace { - int32 face_id = 1; // negative: tracked but not recognized; positive: recognized face - uint32 timestamp = 2; - PoseStruct pose = 3; - CladRect img_rect = 4; // position in image coords - string name = 5; // Empty if none assigned yet - - FacialExpression expression = 6; - - // Individual expression values histogram, sums to 100 (Exception: all zero if expression=Unknown) - repeated uint32 expression_values = 7; - - // Face landmarks - repeated CladPoint left_eye = 8; - repeated CladPoint right_eye = 9; - repeated CladPoint nose = 10; - repeated CladPoint mouth = 11; -} - -// RobotChangedObservedFaceID -// This generally happens when a tracked face (negative ID) is recognized and -// receives a positive ID or when face records get merged -message RobotChangedObservedFaceID { - int32 old_id = 1; - int32 new_id = 2; -} - -message AppIntentRequest { - string intent = 1; - string param = 2; -} - -enum FaceEnrollmentResult -{ - SUCCESS = 0; - - // Failures: - SAW_WRONG_FACE = 1; - SAW_MULTIPLE_FACES = 2; - TIMED_OUT = 3; - SAVE_FAILED = 4; - INCOMPLETE = 5; - CANCELLED = 6; - NAME_IN_USE = 7; - NAMED_STORAGE_FULL = 8; - UNKNOWN_FAILURE = 9; -} - -message AppDisconnected{} - -message AppIntentResponse { - ResponseStatus status = 1; -} - -message FaceEnrollmentCompleted { - FaceEnrollmentResult result = 1; - int32 face_id = 2; - string name = 3; -} - -message CancelFaceEnrollmentRequest { -} - -message CancelFaceEnrollmentResponse { - ResponseStatus status = 1; -} - -message RequestEnrolledNamesRequest { -} - -message LoadedKnownFace { - int64 seconds_since_first_enrolled = 1; - int64 seconds_since_last_updated = 2; - int64 seconds_since_last_seen = 3; - int64 last_seen_seconds_since_epoch = 4; - int32 face_id = 5; - string name = 6; -} - -message RobotRenamedEnrolledFace { - int32 face_id = 1; - string name = 2; -} - -message RequestEnrolledNamesResponse { - ResponseStatus status = 1; - repeated LoadedKnownFace faces = 2; -} - -message UpdateEnrolledFaceByIDRequest { - int32 face_id = 1; - string old_name = 2; - string new_name = 3; -} - -message UpdateEnrolledFaceByIDResponse { - ResponseStatus status = 1; -} - -message EraseEnrolledFaceByIDRequest { - int32 face_id = 1; -} - -message EraseEnrolledFaceByIDResponse { - ResponseStatus status = 1; -} - -message EraseAllEnrolledFacesRequest { -} - -message EraseAllEnrolledFacesResponse { - ResponseStatus status = 1; -} - -message SetFaceToEnrollRequest { - string name = 1; - - int32 observed_id = 2; // The ID of a specific observed face to enroll (0 for next one we see) - int32 save_id = 3; // The ID of an existing face to merge final enrollment into (0 for none, - // i.e. use observedID) - - bool save_to_robot = 4; // Save to robot's NVStorage when done (NOTE: will (re)save everyone enrolled!) - bool say_name = 5; // Play say-name/celebration animations on success before completing - bool use_music = 6; // Starts special music during say-name animations (will leave music playing!) -} -message SetFaceToEnrollResponse { - ResponseStatus status = 1; -} - -enum BehaviorResults -{ - BEHAVIOR_INVALID_STATE = 0; - BEHAVIOR_COMPLETE_STATE = 1; - BEHAVIOR_WONT_ACTIVATE_STATE = 2; -} - -message DriveOffChargerRequest { -} - -message DriveOffChargerResponse { - ResponseStatus status = 1; - BehaviorResults result = 2; -} - -message DriveOnChargerRequest { -} - -message DriveOnChargerResponse { - ResponseStatus status = 1; - BehaviorResults result = 2; -} - - -message WakeWordBegin { -} - -message WakeWordEnd { - bool intent_heard = 1; - string intent_json = 2; -} - -message PhotoInfo { - uint32 photo_id = 1; - uint32 timestamp_utc = 2; - bool photo_copied_to_app = 3; - bool thumb_copied_to_app = 4; -} - -message PhotosInfoRequest { -} - -message PhotosInfoResponse { - ResponseStatus status = 1; - repeated PhotoInfo photo_infos = 2; -} - -message PhotoRequest { - uint32 photo_id = 1; -} - -message PhotoPathMessage { - bool success = 1; - string full_path = 2; -} - -message PhotoResponse { - ResponseStatus status = 1; - bool success = 2; - bytes image = 3; -} - -message ThumbnailRequest { - uint32 photo_id = 1; -} - -message ThumbnailPathMessage { - bool success = 1; - string full_path = 2; -} - -message ThumbnailResponse { - ResponseStatus status = 1; - bool success = 2; - bytes image = 3; -} - -message DeletePhotoRequest { - uint32 photo_id = 1; -} - -message DeletePhotoResponse { - ResponseStatus status = 1; - bool success = 2; -} - -message PhotoTaken { - uint32 photo_id = 1; -} - -// Struct containing all the information relevant to how a -// path should be modified or traversed. -message PathMotionProfile { - float speed_mmps = 1; - float accel_mmps2 = 2; - float decel_mmps2 = 3; - float point_turn_speed_rad_per_sec = 4; - float point_turn_accel_rad_per_sec2 = 5; - float point_turn_decel_rad_per_sec2 = 6; - float dock_speed_mmps = 7; - float dock_accel_mmps2 = 8; - float dock_decel_mmps2 = 9; - float reverse_speed_mmps = 10; - bool is_custom = 11; -} - -// These codes are duplicated from actionResults.clad file and need to be kept in sync -message ActionResult { - enum ActionResultCode { - ACTION_RESULT_SUCCESS = 0; - ACTION_RESULT_RUNNING = 16777216; - - ACTION_RESULT_CANCELLED_WHILE_RUNNING = 33554432; - NOT_STARTED = 33554433; - - ABORT = 50331648; - ANIM_ABORTED = 50331649; - BAD_MARKER = 50331650; - BAD_MESSAGE_TAG = 50331651; - BAD_OBJECT = 50331652; - BAD_POSE = 50331653; - BAD_TAG = 50331654; - CHARGER_UNPLUGGED_ABORT = 50331655; - CLIFF_ALIGN_FAILED_TIMEOUT = 50331656; - CLIFF_ALIGN_FAILED_NO_TURNING = 50331657; - CLIFF_ALIGN_FAILED_OVER_TURNING = 50331658; - CLIFF_ALIGN_FAILED_NO_WHITE = 50331659; - CLIFF_ALIGN_FAILED_STOPPED = 50331660; - FAILED_SETTING_CALIBRATION = 50331661; - FOLLOWING_PATH_BUT_NOT_TRAVERSING = 50331662; - INTERRUPTED = 50331663; - INVALID_OFF_TREADS_STATE = 50331664; - MISMATCHED_UP_AXIS = 50331665; - NO_ANIM_NAME = 50331666; - NO_DISTANCE_SET = 50331667; - NO_FACE = 50331668; - NO_GOAL_SET = 50331669; - NO_PREACTION_POSES = 50331670; - NOT_CARRYING_OBJECT_ABORT = 50331671; - NOT_ON_CHARGER_ABORT = 50331672; - NULL_SUBACTION = 50331673; - PATH_PLANNING_FAILED_ABORT = 50331674; - PICKUP_OBJECT_UNEXPECTEDLY_MOVING = 50331675; - SEND_MESSAGE_TO_ROBOT_FAILED = 50331676; - STILL_CARRYING_OBJECT = 50331677; - TIMEOUT = 50331678; - TRACKS_LOCKED = 50331679; - UNEXPECTED_DOCK_ACTION = 50331680; - UNKNOWN_TOOL_CODE = 50331681; - UPDATE_DERIVED_FAILED = 50331682; - VISUAL_OBSERVATION_FAILED = 50331683; - SHOULDNT_DRIVE_ON_CHARGER = 50331684; - - RETRY = 67108864; - DID_NOT_REACH_PREACTION_POSE = 67108865; - FAILED_TRAVERSING_PATH = 67108866; - LAST_PICK_AND_PLACE_FAILED = 67108867; - MOTOR_STOPPED_MAKING_PROGRESS = 67108868; - NOT_CARRYING_OBJECT_RETRY = 67108869; - NOT_ON_CHARGER_RETRY = 67108870; - PATH_PLANNING_FAILED_RETRY = 67108871; - PLACEMENT_GOAL_NOT_FREE = 67108872; - PICKUP_OBJECT_UNEXPECTEDLY_NOT_MOVING = 67108873; - STILL_ON_CHARGER = 67108874; - UNEXPECTED_PITCH_ANGLE = 67108875; - } - ActionResultCode code = 1; -} - -enum ActionTagConstants { - INVALID_SDK_TAG = 0; - FIRST_SDK_TAG = 2000001; - LAST_SDK_TAG = 3000000; -} - -// GotoPose -message GoToPoseRequest { - float x_mm = 1; - float y_mm = 2; - float rad = 3; - PathMotionProfile motion_prof = 4; - int32 id_tag = 5; - int32 num_retries = 6; -} - -message GoToPoseResponse { - ResponseStatus status = 1; - ActionResult result = 2; -} - -// This is a uint8 in CLAD -enum AlignmentType { - ALIGNMENT_TYPE_UNKNOWN = 0; - ALIGNMENT_TYPE_LIFT_FINGER = 1; // Fingers aligned with object - ALIGNMENT_TYPE_LIFT_PLATE = 2; // Lift plate aligned with object - ALIGNMENT_TYPE_BODY = 3; // Front of body aligned with object - ALIGNMENT_TYPE_CUSTOM = 4; -} - -message DockWithCubeRequest { - int32 object_id = 1; - float distance_from_marker_mm = 2; - float approach_angle_rad = 3; - AlignmentType alignment_type = 4; - bool use_approach_angle = 5; - bool use_pre_dock_pose = 6; - PathMotionProfile motion_prof = 7; - int32 id_tag = 8; - int32 num_retries = 9; -} - -message DockWithCubeResponse { - ResponseStatus status = 1; - ActionResult result = 2; -} - -message DriveStraightRequest { - float speed_mmps = 1; // Speed should be positive - float dist_mm = 2; // Use +ve for forward, -ve for backward - bool should_play_animation = 3; - int32 id_tag = 4; - int32 num_retries = 5; -} - -message DriveStraightResponse { - ResponseStatus status = 1; - ActionResult result = 2; -} - -// TurnInPlace -// If isAbsolute == 0, turns the specified number of radians, relative to the robot's current heading* -// If isAbsolute != 0, turns to the specified absolute orientation. -message TurnInPlaceRequest { - float angle_rad = 1; // relative mode: positive turns left negative turns right - float speed_rad_per_sec = 2; - float accel_rad_per_sec2 = 3; - float tol_rad = 4; // This should be no smaller than POINT_TURN_ANGLE_TOL (DEG_TO_RAD(2.f)). - // Smaller values are ignored except 0 which is interpreted to mean - // default tolerance of POINT_TURN_ANGLE_TOL. - uint32 is_absolute = 5; // 0: angle_rad is relative to current orientation, 1: angle_rad is absolute angle to turn to - int32 id_tag = 6; - int32 num_retries = 7; -} - -message TurnInPlaceResponse { - ResponseStatus status = 1; - ActionResult result = 2; -} - -// SetHeadAngle -message SetHeadAngleRequest { - float angle_rad = 1; - float max_speed_rad_per_sec = 2; - float accel_rad_per_sec2 = 3; - float duration_sec = 4; - int32 id_tag = 5; - int32 num_retries = 6; -} - -message SetHeadAngleResponse { - ResponseStatus status = 1; - ActionResult result = 2; -} - -// SetLiftHeight -message SetLiftHeightRequest { - float height_mm = 1; - float max_speed_rad_per_sec = 2; - float accel_rad_per_sec2 = 3; - float duration_sec = 4; - int32 id_tag = 5; - int32 num_retries = 6; -} - -message SetLiftHeightResponse { - ResponseStatus status = 1; - ActionResult result = 2; -} - -enum BatteryLevel { - BATTERY_LEVEL_UNKNOWN = 0; - BATTERY_LEVEL_LOW = 1; - BATTERY_LEVEL_NOMINAL = 2; - BATTERY_LEVEL_FULL = 3; -} - -message NetworkStats { - int32 g_net_stat1_num_connections = 1; - float g_net_stat2_latency_avg = 2; - float g_net_stat3_latency_sd = 3; - float g_net_stat4_latency_min = 4; - float g_net_stat5_latency_max = 5; - float g_net_stat6_ping_arrived_pc = 6; - float g_net_stat7_ext_queued_avg_ms = 7; - float g_net_stat8_ext_queued_min_ms = 8; - float g_net_stat9_ext_queued_max_ms = 9; - float g_net_stata_queued_avg_ms = 10; - float g_net_statb_queued_min_ms = 11; - float g_net_statc_queued_max_ms = 12; -} - -message BatteryStateRequest {} - -message BatteryStateResponse { - ResponseStatus status = 1; - BatteryLevel battery_level = 2; - float battery_volts = 3; - bool is_charging = 4; - bool is_on_charger_platform = 5; - float suggested_charger_sec = 6; - CubeBattery cube_battery = 7; -} - -message CubeBattery { - enum CubeBatteryLevel { - Low = 0; - Normal = 1; - } - CubeBatteryLevel level = 1; - string factory_id = 2; - float battery_volts = 3; - float time_since_last_reading_sec = 4; -} - -message VersionStateRequest {} - -message VersionStateResponse { - ResponseStatus status = 1; - string os_version = 2; - string engine_build_id = 3; -} - -message NetworkStateRequest {} - -message NetworkStateResponse { - ResponseStatus status = 1; - NetworkStats network_stats = 2; -} - -message SayTextRequest { - string text = 1; - bool use_vector_voice = 2; - float duration_scalar = 3; -} - -message SayTextResponse { - enum UtteranceState - { - INVALID = 0; - GENERATING = 1; - READY = 2; - PLAYING = 3; - FINISHED = 4; - } - ResponseStatus status = 1; - UtteranceState state = 2; -} - -message StimulationInfo { - repeated string emotion_events = 1; // may be empty - float value = 2; - float velocity = 3; // value per second - float accel = 4; // value per sec per sec - float value_before_event = 5; // matches value if there were no emotion events - float min_value = 6; - float max_value = 7; -} - -// Constants associated with the audio feed -enum AudioConstants { - AUDIO_CONSTANTS_NULL = 0; // error value - MIC_DETECTION_DIRECTIONS = 12; // The number of audio directions the four microphones can isolate - SAMPLE_COUNTS_PER_ENGINE_MESSAGE = 160; // The number of audio samples in each internal audio chunk - SAMPLE_COUNTS_PER_SDK_MESSAGE = 1600; // The number of audio samples delivered in each audio feed response - MICROPHONE_SAMPLE_RATE = 15625; // The sampling rate victor's microphones record at - PROCESSED_SAMPLE_RATE = 16000; // The sampling rate the robot processes audio at -} - -// The robot can process audio in a variety of ways -enum AudioProcessingMode { - AUDIO_UNKNOWN = 0; // error value - AUDIO_OFF = 1; // deactivates audio SDK mode - AUDIO_FAST_MODE = 2; // unprocessed single microphone data - most performant on robot - AUDIO_DIRECTIONAL_MODE = 3; // beamforming support for focusing on specific direction - sounds cleanest - AUDIO_VOICE_DETECT_MODE = 4; // multi-microphone non-beamforming - best for voice detection programs -} - -// Request how the robot should process and send audio -message AudioSendModeRequest { - AudioProcessingMode mode = 1; -} - -// Event sent when the robot changes the mode it's processing and sending audio -message AudioSendModeChanged { - AudioProcessingMode mode = 1; -} - -// One frame of audio data and associated metadata -message AudioChunk { - uint32 robot_time_stamp = 1; // robot time at the final chunk of this audio sample group transmission - uint32 group_id = 2; // the id of this sample transmission group - uint32 chunk_id = 3; // the current batched chunk id within in this group id - uint32 audio_chunk_count = 4; // number of chunks batched within this group id - bytes signal_power = 5; // mono audio amplitude samples - bytes direction_strengths = 6; // histogram data of which directions this audio chunk came from - uint32 source_direction = 7; // 0-11, with 12 representing "invalid" - uint32 source_confidence = 8; // accuracy of the calculated source_direction - uint32 noise_floor_power = 9; // power value, convert to db with log_10(value) -} - -// Request the robot to send audio data -message AudioFeedRequest {} - -// Streamed audio data from the robot -message AudioFeedResponse { - option (streamed) = true; - - uint32 robot_time_stamp = 1; // robot time at the transmission of this audio sample group - uint32 group_id = 2; // the index of this audio feed response - bytes signal_power = 3; // mono audio amplitude samples - bytes direction_strengths = 4; // histogram data of which directions this audio chunk came from - uint32 source_direction = 5; // 0-11, with 12 representing "invalid" - uint32 source_confidence = 6; // accuracy of the calculated source_direction - uint32 noise_floor_power = 7; // power value, convert to db with log_10(value) -} - - -// When enabled, RobotObservedObject messages will be produced -message EnableMarkerDetectionRequest -{ - bool enable = 1; -} - -message EnableMarkerDetectionResponse -{ - ResponseStatus status = 1; -} - -// When enabled, RobotObservedFace messages will be produced -message EnableFaceDetectionRequest -{ - bool enable = 1; - - bool enable_smile_detection = 2; - bool enable_expression_estimation = 3; - bool enable_blink_detection = 4; - bool enable_gaze_detection = 5; -} - -message EnableFaceDetectionResponse -{ - ResponseStatus status = 1; -} - -// When enabled, RobotObservedMotion messages will be produced -message EnableMotionDetectionRequest -{ - bool enable = 1; -} - -message EnableMotionDetectionResponse -{ - ResponseStatus status = 1; -} - -// When enabled, camera feed will appear on the robot's face, along with any -// detections that are enabled from above messages -message EnableMirrorModeRequest -{ - bool enable = 1; -} - -message EnableMirrorModeResponse -{ - ResponseStatus status = 1; -} - -// Sent if MirrorMode (camera feed displayed on face) is currently enabled but is automatically -// being disabled -message MirrorModeDisabled -{ - -} - -// Toggle image streaming at the given resolution -message EnableImageStreamingRequest -{ - bool enable = 1; -} - -message EnableImageStreamingResponse -{ - ResponseStatus status = 1; -} - -// Sent when vision modes are automatically disabled due to the SDK no longer having control -// of the robot -message VisionModesAutoDisabled -{ - -} - -// One frame of image data and associated metadata -message ImageChunk { - enum ImageEncoding - { - NONE_IMAGE_ENCODING = 0; - RAW_GRAY = 1; // no compression - RAW_RGB = 2; // no compression, just [RGBRGBRG...] - YUYV = 3; - YUV420SP = 4; - BAYER = 5; - JPEG_GRAY = 6; - JPEG_COLOR = 7; - JPEG_COLOR_HALF_WIDTH = 8; - JPEG_MINIMIZED_GRAY = 9; // Minimized grayscale JPEG - no header, no footer, no byte stuffing - JPEG_MINIMIZED_COLOR = 10; // Minimized grayscale JPEG - no header, no footer, no byte stuffing, with added color data - } - uint32 frame_time_stamp = 1; - uint32 image_id = 2; - uint32 width = 3; - uint32 height = 4; - ImageEncoding image_encoding = 5; - uint32 display_index = 6; - uint32 image_chunk_count = 7; - uint32 chunk_id = 8; - bytes data = 9; -} - -message CameraFeedRequest {} - -message CameraFeedResponse { - option (streamed) = true; - - uint32 frame_time_stamp = 1; - uint32 image_id = 2; - ImageChunk.ImageEncoding image_encoding = 3; - bytes data = 4; -} - -message SetEyeColorRequest { - float hue = 1; - float saturation = 2; -} - -message SetEyeColorResponse { - ResponseStatus status = 1; -} - -message SDKInitializationRequest { - string sdk_module_version = 1; - string python_version = 2; - string python_implementation = 3; - string os_version = 4; - string cpu_version = 5; -} - -message SDKInitializationResponse { - ResponseStatus status = 1; -} - diff --git a/anki_vector/messaging/messages_pb2.py b/anki_vector/messaging/messages_pb2.py deleted file mode 100644 index 7133b77..0000000 --- a/anki_vector/messaging/messages_pb2.py +++ /dev/null @@ -1,6715 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/messages.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -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() - - -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 - - -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\"\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,]) - -_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', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='EXPRESSION_UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXPRESSION_NEUTRAL', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXPRESSION_HAPPINESS', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXPRESSION_SURPRISE', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXPRESSION_ANGER', index=4, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXPRESSION_SADNESS', index=5, number=5, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXPRESSION_COUNT', index=6, number=5, - options=None, - type=None), - ], - containing_type=None, - options=_descriptor._ParseOptions(descriptor_pb2.EnumOptions(), _b('\020\001')), - serialized_start=15738, - serialized_end=15927, -) -_sym_db.RegisterEnumDescriptor(_FACIALEXPRESSION) - -FacialExpression = enum_type_wrapper.EnumTypeWrapper(_FACIALEXPRESSION) -_FACEENROLLMENTRESULT = _descriptor.EnumDescriptor( - name='FaceEnrollmentResult', - full_name='Anki.Vector.external_interface.FaceEnrollmentResult', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='SUCCESS', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SAW_WRONG_FACE', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SAW_MULTIPLE_FACES', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TIMED_OUT', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SAVE_FAILED', index=4, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INCOMPLETE', index=5, number=5, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CANCELLED', index=6, number=6, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAME_IN_USE', index=7, number=7, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAMED_STORAGE_FULL', index=8, number=8, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNKNOWN_FAILURE', index=9, number=9, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=15930, - serialized_end=16134, -) -_sym_db.RegisterEnumDescriptor(_FACEENROLLMENTRESULT) - -FaceEnrollmentResult = enum_type_wrapper.EnumTypeWrapper(_FACEENROLLMENTRESULT) -_BEHAVIORRESULTS = _descriptor.EnumDescriptor( - name='BehaviorResults', - full_name='Anki.Vector.external_interface.BehaviorResults', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='BEHAVIOR_INVALID_STATE', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BEHAVIOR_COMPLETE_STATE', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BEHAVIOR_WONT_ACTIVATE_STATE', index=2, number=2, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=16136, - serialized_end=16244, -) -_sym_db.RegisterEnumDescriptor(_BEHAVIORRESULTS) - -BehaviorResults = enum_type_wrapper.EnumTypeWrapper(_BEHAVIORRESULTS) -_ACTIONTAGCONSTANTS = _descriptor.EnumDescriptor( - name='ActionTagConstants', - full_name='Anki.Vector.external_interface.ActionTagConstants', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='INVALID_SDK_TAG', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FIRST_SDK_TAG', index=1, number=2000001, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='LAST_SDK_TAG', index=2, number=3000000, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=16246, - serialized_end=16329, -) -_sym_db.RegisterEnumDescriptor(_ACTIONTAGCONSTANTS) - -ActionTagConstants = enum_type_wrapper.EnumTypeWrapper(_ACTIONTAGCONSTANTS) -_ALIGNMENTTYPE = _descriptor.EnumDescriptor( - name='AlignmentType', - full_name='Anki.Vector.external_interface.AlignmentType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='ALIGNMENT_TYPE_UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ALIGNMENT_TYPE_LIFT_FINGER', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ALIGNMENT_TYPE_LIFT_PLATE', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ALIGNMENT_TYPE_BODY', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ALIGNMENT_TYPE_CUSTOM', index=4, number=4, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=16332, - serialized_end=16490, -) -_sym_db.RegisterEnumDescriptor(_ALIGNMENTTYPE) - -AlignmentType = enum_type_wrapper.EnumTypeWrapper(_ALIGNMENTTYPE) -_BATTERYLEVEL = _descriptor.EnumDescriptor( - name='BatteryLevel', - full_name='Anki.Vector.external_interface.BatteryLevel', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='BATTERY_LEVEL_UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BATTERY_LEVEL_LOW', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BATTERY_LEVEL_NOMINAL', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BATTERY_LEVEL_FULL', index=3, number=3, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=16492, - serialized_end=16607, -) -_sym_db.RegisterEnumDescriptor(_BATTERYLEVEL) - -BatteryLevel = enum_type_wrapper.EnumTypeWrapper(_BATTERYLEVEL) -_AUDIOCONSTANTS = _descriptor.EnumDescriptor( - name='AudioConstants', - full_name='Anki.Vector.external_interface.AudioConstants', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='AUDIO_CONSTANTS_NULL', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MIC_DETECTION_DIRECTIONS', index=1, number=12, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SAMPLE_COUNTS_PER_ENGINE_MESSAGE', index=2, number=160, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SAMPLE_COUNTS_PER_SDK_MESSAGE', index=3, number=1600, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MICROPHONE_SAMPLE_RATE', index=4, number=15625, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PROCESSED_SAMPLE_RATE', index=5, number=16000, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=16610, - serialized_end=16814, -) -_sym_db.RegisterEnumDescriptor(_AUDIOCONSTANTS) - -AudioConstants = enum_type_wrapper.EnumTypeWrapper(_AUDIOCONSTANTS) -_AUDIOPROCESSINGMODE = _descriptor.EnumDescriptor( - name='AudioProcessingMode', - full_name='Anki.Vector.external_interface.AudioProcessingMode', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='AUDIO_UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='AUDIO_OFF', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='AUDIO_FAST_MODE', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='AUDIO_DIRECTIONAL_MODE', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='AUDIO_VOICE_DETECT_MODE', index=4, number=4, - options=None, - type=None), - ], - containing_type=None, - options=None, - 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 -EXPRESSION_SURPRISE = 3 -EXPRESSION_ANGER = 4 -EXPRESSION_SADNESS = 5 -EXPRESSION_COUNT = 5 -SUCCESS = 0 -SAW_WRONG_FACE = 1 -SAW_MULTIPLE_FACES = 2 -TIMED_OUT = 3 -SAVE_FAILED = 4 -INCOMPLETE = 5 -CANCELLED = 6 -NAME_IN_USE = 7 -NAMED_STORAGE_FULL = 8 -UNKNOWN_FAILURE = 9 -BEHAVIOR_INVALID_STATE = 0 -BEHAVIOR_COMPLETE_STATE = 1 -BEHAVIOR_WONT_ACTIVATE_STATE = 2 -INVALID_SDK_TAG = 0 -FIRST_SDK_TAG = 2000001 -LAST_SDK_TAG = 3000000 -ALIGNMENT_TYPE_UNKNOWN = 0 -ALIGNMENT_TYPE_LIFT_FINGER = 1 -ALIGNMENT_TYPE_LIFT_PLATE = 2 -ALIGNMENT_TYPE_BODY = 3 -ALIGNMENT_TYPE_CUSTOM = 4 -BATTERY_LEVEL_UNKNOWN = 0 -BATTERY_LEVEL_LOW = 1 -BATTERY_LEVEL_NOMINAL = 2 -BATTERY_LEVEL_FULL = 3 -AUDIO_CONSTANTS_NULL = 0 -MIC_DETECTION_DIRECTIONS = 12 -SAMPLE_COUNTS_PER_ENGINE_MESSAGE = 160 -SAMPLE_COUNTS_PER_SDK_MESSAGE = 1600 -MICROPHONE_SAMPLE_RATE = 15625 -PROCESSED_SAMPLE_RATE = 16000 -AUDIO_UNKNOWN = 0 -AUDIO_OFF = 1 -AUDIO_FAST_MODE = 2 -AUDIO_DIRECTIONAL_MODE = 3 -AUDIO_VOICE_DETECT_MODE = 4 - - -_ACTIONRESULT_ACTIONRESULTCODE = _descriptor.EnumDescriptor( - name='ActionResultCode', - full_name='Anki.Vector.external_interface.ActionResult.ActionResultCode', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='ACTION_RESULT_SUCCESS', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ACTION_RESULT_RUNNING', index=1, number=16777216, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ACTION_RESULT_CANCELLED_WHILE_RUNNING', index=2, number=33554432, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_STARTED', index=3, number=33554433, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ABORT', index=4, number=50331648, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ANIM_ABORTED', index=5, number=50331649, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BAD_MARKER', index=6, number=50331650, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BAD_MESSAGE_TAG', index=7, number=50331651, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BAD_OBJECT', index=8, number=50331652, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BAD_POSE', index=9, number=50331653, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BAD_TAG', index=10, number=50331654, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CHARGER_UNPLUGGED_ABORT', index=11, number=50331655, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLIFF_ALIGN_FAILED_TIMEOUT', index=12, number=50331656, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLIFF_ALIGN_FAILED_NO_TURNING', index=13, number=50331657, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLIFF_ALIGN_FAILED_OVER_TURNING', index=14, number=50331658, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLIFF_ALIGN_FAILED_NO_WHITE', index=15, number=50331659, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLIFF_ALIGN_FAILED_STOPPED', index=16, number=50331660, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FAILED_SETTING_CALIBRATION', index=17, number=50331661, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FOLLOWING_PATH_BUT_NOT_TRAVERSING', index=18, number=50331662, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INTERRUPTED', index=19, number=50331663, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INVALID_OFF_TREADS_STATE', index=20, number=50331664, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MISMATCHED_UP_AXIS', index=21, number=50331665, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NO_ANIM_NAME', index=22, number=50331666, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NO_DISTANCE_SET', index=23, number=50331667, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NO_FACE', index=24, number=50331668, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NO_GOAL_SET', index=25, number=50331669, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NO_PREACTION_POSES', index=26, number=50331670, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_CARRYING_OBJECT_ABORT', index=27, number=50331671, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_ON_CHARGER_ABORT', index=28, number=50331672, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NULL_SUBACTION', index=29, number=50331673, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PATH_PLANNING_FAILED_ABORT', index=30, number=50331674, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PICKUP_OBJECT_UNEXPECTEDLY_MOVING', index=31, number=50331675, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SEND_MESSAGE_TO_ROBOT_FAILED', index=32, number=50331676, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='STILL_CARRYING_OBJECT', index=33, number=50331677, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TIMEOUT', index=34, number=50331678, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TRACKS_LOCKED', index=35, number=50331679, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNEXPECTED_DOCK_ACTION', index=36, number=50331680, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNKNOWN_TOOL_CODE', index=37, number=50331681, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UPDATE_DERIVED_FAILED', index=38, number=50331682, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='VISUAL_OBSERVATION_FAILED', index=39, number=50331683, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SHOULDNT_DRIVE_ON_CHARGER', index=40, number=50331684, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RETRY', index=41, number=67108864, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DID_NOT_REACH_PREACTION_POSE', index=42, number=67108865, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FAILED_TRAVERSING_PATH', index=43, number=67108866, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='LAST_PICK_AND_PLACE_FAILED', index=44, number=67108867, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MOTOR_STOPPED_MAKING_PROGRESS', index=45, number=67108868, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_CARRYING_OBJECT_RETRY', index=46, number=67108869, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_ON_CHARGER_RETRY', index=47, number=67108870, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PATH_PLANNING_FAILED_RETRY', index=48, number=67108871, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PLACEMENT_GOAL_NOT_FREE', index=49, number=67108872, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PICKUP_OBJECT_UNEXPECTEDLY_NOT_MOVING', index=50, number=67108873, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='STILL_ON_CHARGER', index=51, number=67108874, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNEXPECTED_PITCH_ANGLE', index=52, number=67108875, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=7112, - serialized_end=8674, -) -_sym_db.RegisterEnumDescriptor(_ACTIONRESULT_ACTIONRESULTCODE) - -_CUBEBATTERY_CUBEBATTERYLEVEL = _descriptor.EnumDescriptor( - name='CubeBatteryLevel', - full_name='Anki.Vector.external_interface.CubeBattery.CubeBatteryLevel', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='Low', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='Normal', index=1, number=1, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=11684, - serialized_end=11723, -) -_sym_db.RegisterEnumDescriptor(_CUBEBATTERY_CUBEBATTERYLEVEL) - -_SAYTEXTRESPONSE_UTTERANCESTATE = _descriptor.EnumDescriptor( - name='UtteranceState', - full_name='Anki.Vector.external_interface.SayTextResponse.UtteranceState', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='INVALID', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='GENERATING', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='READY', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PLAYING', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FINISHED', index=4, number=4, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=12309, - serialized_end=12392, -) -_sym_db.RegisterEnumDescriptor(_SAYTEXTRESPONSE_UTTERANCESTATE) - -_IMAGECHUNK_IMAGEENCODING = _descriptor.EnumDescriptor( - name='ImageEncoding', - full_name='Anki.Vector.external_interface.ImageChunk.ImageEncoding', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='NONE_IMAGE_ENCODING', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RAW_GRAY', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RAW_RGB', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='YUYV', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='YUV420SP', index=4, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BAYER', index=5, number=5, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='JPEG_GRAY', index=6, number=6, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='JPEG_COLOR', index=7, number=7, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='JPEG_COLOR_HALF_WIDTH', index=8, number=8, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='JPEG_MINIMIZED_GRAY', index=9, number=9, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='JPEG_MINIMIZED_COLOR', index=10, number=10, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=14339, - serialized_end=14550, -) -_sym_db.RegisterEnumDescriptor(_IMAGECHUNK_IMAGEENCODING) - - -_KEEPALIVEPING = _descriptor.Descriptor( - name='KeepAlivePing', - full_name='Anki.Vector.external_interface.KeepAlivePing', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=157, - serialized_end=172, -) - - -_ANIMATION = _descriptor.Descriptor( - name='Animation', - full_name='Anki.Vector.external_interface.Animation', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='Anki.Vector.external_interface.Animation.name', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=174, - serialized_end=199, -) - - -_DRIVEWHEELSREQUEST = _descriptor.Descriptor( - name='DriveWheelsRequest', - full_name='Anki.Vector.external_interface.DriveWheelsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='left_wheel_mmps', full_name='Anki.Vector.external_interface.DriveWheelsRequest.left_wheel_mmps', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='right_wheel_mmps', full_name='Anki.Vector.external_interface.DriveWheelsRequest.right_wheel_mmps', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='left_wheel_mmps2', full_name='Anki.Vector.external_interface.DriveWheelsRequest.left_wheel_mmps2', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='right_wheel_mmps2', full_name='Anki.Vector.external_interface.DriveWheelsRequest.right_wheel_mmps2', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=201, - serialized_end=325, -) - - -_DRIVEWHEELSRESPONSE = _descriptor.Descriptor( - name='DriveWheelsResponse', - full_name='Anki.Vector.external_interface.DriveWheelsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DriveWheelsResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=327, - serialized_end=412, -) - - -_MOVEHEADREQUEST = _descriptor.Descriptor( - name='MoveHeadRequest', - full_name='Anki.Vector.external_interface.MoveHeadRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='speed_rad_per_sec', full_name='Anki.Vector.external_interface.MoveHeadRequest.speed_rad_per_sec', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=414, - serialized_end=458, -) - - -_MOVEHEADRESPONSE = _descriptor.Descriptor( - name='MoveHeadResponse', - full_name='Anki.Vector.external_interface.MoveHeadResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.MoveHeadResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=460, - serialized_end=542, -) - - -_MOVELIFTREQUEST = _descriptor.Descriptor( - name='MoveLiftRequest', - full_name='Anki.Vector.external_interface.MoveLiftRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='speed_rad_per_sec', full_name='Anki.Vector.external_interface.MoveLiftRequest.speed_rad_per_sec', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=544, - serialized_end=588, -) - - -_MOVELIFTRESPONSE = _descriptor.Descriptor( - name='MoveLiftResponse', - full_name='Anki.Vector.external_interface.MoveLiftResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.MoveLiftResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=590, - serialized_end=672, -) - - -_PLAYANIMATIONREQUEST = _descriptor.Descriptor( - name='PlayAnimationRequest', - full_name='Anki.Vector.external_interface.PlayAnimationRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='animation', full_name='Anki.Vector.external_interface.PlayAnimationRequest.animation', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='loops', full_name='Anki.Vector.external_interface.PlayAnimationRequest.loops', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ignore_body_track', full_name='Anki.Vector.external_interface.PlayAnimationRequest.ignore_body_track', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ignore_head_track', full_name='Anki.Vector.external_interface.PlayAnimationRequest.ignore_head_track', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ignore_lift_track', full_name='Anki.Vector.external_interface.PlayAnimationRequest.ignore_lift_track', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=675, - serialized_end=855, -) - - -_PLAYANIMATIONRESPONSE = _descriptor.Descriptor( - name='PlayAnimationResponse', - full_name='Anki.Vector.external_interface.PlayAnimationResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.PlayAnimationResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.PlayAnimationResponse.result', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=858, - serialized_end=1010, -) - - -_LISTANIMATIONSREQUEST = _descriptor.Descriptor( - name='ListAnimationsRequest', - full_name='Anki.Vector.external_interface.ListAnimationsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1012, - serialized_end=1035, -) - - -_LISTANIMATIONSRESPONSE = _descriptor.Descriptor( - name='ListAnimationsResponse', - full_name='Anki.Vector.external_interface.ListAnimationsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.ListAnimationsResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='animation_names', full_name='Anki.Vector.external_interface.ListAnimationsResponse.animation_names', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1038, - serialized_end=1194, -) - - -_DISPLAYFACEIMAGERGBREQUEST = _descriptor.Descriptor( - name='DisplayFaceImageRGBRequest', - full_name='Anki.Vector.external_interface.DisplayFaceImageRGBRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='face_data', full_name='Anki.Vector.external_interface.DisplayFaceImageRGBRequest.face_data', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='duration_ms', full_name='Anki.Vector.external_interface.DisplayFaceImageRGBRequest.duration_ms', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='interrupt_running', full_name='Anki.Vector.external_interface.DisplayFaceImageRGBRequest.interrupt_running', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1196, - serialized_end=1291, -) - - -_DISPLAYFACEIMAGERGBRESPONSE = _descriptor.Descriptor( - name='DisplayFaceImageRGBResponse', - full_name='Anki.Vector.external_interface.DisplayFaceImageRGBResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DisplayFaceImageRGBResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1293, - serialized_end=1386, -) - - -_MEETVICTORFACESCANSTARTED = _descriptor.Descriptor( - name='MeetVictorFaceScanStarted', - full_name='Anki.Vector.external_interface.MeetVictorFaceScanStarted', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1388, - serialized_end=1415, -) - - -_MEETVICTORFACESCANCOMPLETE = _descriptor.Descriptor( - name='MeetVictorFaceScanComplete', - full_name='Anki.Vector.external_interface.MeetVictorFaceScanComplete', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1417, - serialized_end=1445, -) - - -_STATUS = _descriptor.Descriptor( - name='Status', - full_name='Anki.Vector.external_interface.Status', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='meet_victor_face_scan_started', full_name='Anki.Vector.external_interface.Status.meet_victor_face_scan_started', index=0, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='meet_victor_face_scan_complete', full_name='Anki.Vector.external_interface.Status.meet_victor_face_scan_complete', index=1, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='face_enrollment_completed', full_name='Anki.Vector.external_interface.Status.face_enrollment_completed', index=2, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - _descriptor.OneofDescriptor( - name='status_type', full_name='Anki.Vector.external_interface.Status.status_type', - index=0, containing_type=None, fields=[]), - ], - serialized_start=1448, - serialized_end=1767, -) - - -_WAKEWORD = _descriptor.Descriptor( - name='WakeWord', - full_name='Anki.Vector.external_interface.WakeWord', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='wake_word_begin', full_name='Anki.Vector.external_interface.WakeWord.wake_word_begin', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='wake_word_end', full_name='Anki.Vector.external_interface.WakeWord.wake_word_end', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - _descriptor.OneofDescriptor( - name='wake_word_type', full_name='Anki.Vector.external_interface.WakeWord.wake_word_type', - index=0, containing_type=None, fields=[]), - ], - serialized_start=1770, - serialized_end=1942, -) - - -_TIMESTAMPEDSTATUS = _descriptor.Descriptor( - name='TimeStampedStatus', - full_name='Anki.Vector.external_interface.TimeStampedStatus', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.TimeStampedStatus.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='timestamp_utc', full_name='Anki.Vector.external_interface.TimeStampedStatus.timestamp_utc', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1944, - serialized_end=2042, -) - - -_POSESTRUCT = _descriptor.Descriptor( - name='PoseStruct', - full_name='Anki.Vector.external_interface.PoseStruct', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='x', full_name='Anki.Vector.external_interface.PoseStruct.x', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='y', full_name='Anki.Vector.external_interface.PoseStruct.y', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='z', full_name='Anki.Vector.external_interface.PoseStruct.z', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='q0', full_name='Anki.Vector.external_interface.PoseStruct.q0', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='q1', full_name='Anki.Vector.external_interface.PoseStruct.q1', index=4, - number=5, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='q2', full_name='Anki.Vector.external_interface.PoseStruct.q2', index=5, - number=6, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='q3', full_name='Anki.Vector.external_interface.PoseStruct.q3', index=6, - number=7, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='origin_id', full_name='Anki.Vector.external_interface.PoseStruct.origin_id', index=7, - number=8, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2044, - serialized_end=2156, -) - - -_ACCELDATA = _descriptor.Descriptor( - name='AccelData', - full_name='Anki.Vector.external_interface.AccelData', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='x', full_name='Anki.Vector.external_interface.AccelData.x', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='y', full_name='Anki.Vector.external_interface.AccelData.y', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='z', full_name='Anki.Vector.external_interface.AccelData.z', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2158, - serialized_end=2202, -) - - -_GYRODATA = _descriptor.Descriptor( - name='GyroData', - full_name='Anki.Vector.external_interface.GyroData', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='x', full_name='Anki.Vector.external_interface.GyroData.x', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='y', full_name='Anki.Vector.external_interface.GyroData.y', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='z', full_name='Anki.Vector.external_interface.GyroData.z', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2204, - serialized_end=2247, -) - - -_PROXDATA = _descriptor.Descriptor( - name='ProxData', - full_name='Anki.Vector.external_interface.ProxData', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='distance_mm', full_name='Anki.Vector.external_interface.ProxData.distance_mm', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='signal_quality', full_name='Anki.Vector.external_interface.ProxData.signal_quality', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_in_valid_range', full_name='Anki.Vector.external_interface.ProxData.is_in_valid_range', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_valid_signal_quality', full_name='Anki.Vector.external_interface.ProxData.is_valid_signal_quality', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_lift_in_fov', full_name='Anki.Vector.external_interface.ProxData.is_lift_in_fov', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_too_pitched', full_name='Anki.Vector.external_interface.ProxData.is_too_pitched', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2250, - serialized_end=2413, -) - - -_TOUCHDATA = _descriptor.Descriptor( - name='TouchData', - full_name='Anki.Vector.external_interface.TouchData', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='raw_touch_value', full_name='Anki.Vector.external_interface.TouchData.raw_touch_value', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_being_touched', full_name='Anki.Vector.external_interface.TouchData.is_being_touched', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2415, - serialized_end=2477, -) - - -_ROBOTSTATE = _descriptor.Descriptor( - name='RobotState', - full_name='Anki.Vector.external_interface.RobotState', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='pose', full_name='Anki.Vector.external_interface.RobotState.pose', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='pose_angle_rad', full_name='Anki.Vector.external_interface.RobotState.pose_angle_rad', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='pose_pitch_rad', full_name='Anki.Vector.external_interface.RobotState.pose_pitch_rad', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='left_wheel_speed_mmps', full_name='Anki.Vector.external_interface.RobotState.left_wheel_speed_mmps', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='right_wheel_speed_mmps', full_name='Anki.Vector.external_interface.RobotState.right_wheel_speed_mmps', index=4, - number=5, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='head_angle_rad', full_name='Anki.Vector.external_interface.RobotState.head_angle_rad', index=5, - number=6, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='lift_height_mm', full_name='Anki.Vector.external_interface.RobotState.lift_height_mm', index=6, - number=7, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='accel', full_name='Anki.Vector.external_interface.RobotState.accel', index=7, - number=8, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='gyro', full_name='Anki.Vector.external_interface.RobotState.gyro', index=8, - number=9, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='carrying_object_id', full_name='Anki.Vector.external_interface.RobotState.carrying_object_id', index=9, - number=10, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='carrying_object_on_top_id', full_name='Anki.Vector.external_interface.RobotState.carrying_object_on_top_id', index=10, - number=11, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='head_tracking_object_id', full_name='Anki.Vector.external_interface.RobotState.head_tracking_object_id', index=11, - number=12, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='localized_to_object_id', full_name='Anki.Vector.external_interface.RobotState.localized_to_object_id', index=12, - number=13, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='last_image_time_stamp', full_name='Anki.Vector.external_interface.RobotState.last_image_time_stamp', index=13, - number=14, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.RobotState.status', index=14, - number=15, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='prox_data', full_name='Anki.Vector.external_interface.RobotState.prox_data', index=15, - number=16, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='touch_data', full_name='Anki.Vector.external_interface.RobotState.touch_data', index=16, - number=17, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2480, - serialized_end=3122, -) - - -_CLADPOINT = _descriptor.Descriptor( - name='CladPoint', - full_name='Anki.Vector.external_interface.CladPoint', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='x', full_name='Anki.Vector.external_interface.CladPoint.x', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='y', full_name='Anki.Vector.external_interface.CladPoint.y', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3124, - serialized_end=3157, -) - - -_CLADRECT = _descriptor.Descriptor( - name='CladRect', - full_name='Anki.Vector.external_interface.CladRect', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='x_top_left', full_name='Anki.Vector.external_interface.CladRect.x_top_left', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='y_top_left', full_name='Anki.Vector.external_interface.CladRect.y_top_left', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='width', full_name='Anki.Vector.external_interface.CladRect.width', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='height', full_name='Anki.Vector.external_interface.CladRect.height', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3159, - serialized_end=3240, -) - - -_ROBOTOBSERVEDFACE = _descriptor.Descriptor( - name='RobotObservedFace', - full_name='Anki.Vector.external_interface.RobotObservedFace', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='face_id', full_name='Anki.Vector.external_interface.RobotObservedFace.face_id', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='timestamp', full_name='Anki.Vector.external_interface.RobotObservedFace.timestamp', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='pose', full_name='Anki.Vector.external_interface.RobotObservedFace.pose', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='img_rect', full_name='Anki.Vector.external_interface.RobotObservedFace.img_rect', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='Anki.Vector.external_interface.RobotObservedFace.name', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='expression', full_name='Anki.Vector.external_interface.RobotObservedFace.expression', index=5, - number=6, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='expression_values', full_name='Anki.Vector.external_interface.RobotObservedFace.expression_values', index=6, - number=7, type=13, cpp_type=3, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='left_eye', full_name='Anki.Vector.external_interface.RobotObservedFace.left_eye', index=7, - number=8, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='right_eye', full_name='Anki.Vector.external_interface.RobotObservedFace.right_eye', index=8, - number=9, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='nose', full_name='Anki.Vector.external_interface.RobotObservedFace.nose', index=9, - number=10, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='mouth', full_name='Anki.Vector.external_interface.RobotObservedFace.mouth', index=10, - number=11, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3243, - serialized_end=3765, -) - - -_ROBOTCHANGEDOBSERVEDFACEID = _descriptor.Descriptor( - name='RobotChangedObservedFaceID', - full_name='Anki.Vector.external_interface.RobotChangedObservedFaceID', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='old_id', full_name='Anki.Vector.external_interface.RobotChangedObservedFaceID.old_id', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='new_id', full_name='Anki.Vector.external_interface.RobotChangedObservedFaceID.new_id', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3767, - serialized_end=3827, -) - - -_APPINTENTREQUEST = _descriptor.Descriptor( - name='AppIntentRequest', - full_name='Anki.Vector.external_interface.AppIntentRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='intent', full_name='Anki.Vector.external_interface.AppIntentRequest.intent', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='param', full_name='Anki.Vector.external_interface.AppIntentRequest.param', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3829, - serialized_end=3878, -) - - -_APPDISCONNECTED = _descriptor.Descriptor( - name='AppDisconnected', - full_name='Anki.Vector.external_interface.AppDisconnected', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3880, - serialized_end=3897, -) - - -_APPINTENTRESPONSE = _descriptor.Descriptor( - name='AppIntentResponse', - full_name='Anki.Vector.external_interface.AppIntentResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.AppIntentResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3899, - serialized_end=3982, -) - - -_FACEENROLLMENTCOMPLETED = _descriptor.Descriptor( - name='FaceEnrollmentCompleted', - full_name='Anki.Vector.external_interface.FaceEnrollmentCompleted', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.FaceEnrollmentCompleted.result', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='face_id', full_name='Anki.Vector.external_interface.FaceEnrollmentCompleted.face_id', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='Anki.Vector.external_interface.FaceEnrollmentCompleted.name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3984, - serialized_end=4110, -) - - -_CANCELFACEENROLLMENTREQUEST = _descriptor.Descriptor( - name='CancelFaceEnrollmentRequest', - full_name='Anki.Vector.external_interface.CancelFaceEnrollmentRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4112, - serialized_end=4141, -) - - -_CANCELFACEENROLLMENTRESPONSE = _descriptor.Descriptor( - name='CancelFaceEnrollmentResponse', - full_name='Anki.Vector.external_interface.CancelFaceEnrollmentResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.CancelFaceEnrollmentResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4143, - serialized_end=4237, -) - - -_REQUESTENROLLEDNAMESREQUEST = _descriptor.Descriptor( - name='RequestEnrolledNamesRequest', - full_name='Anki.Vector.external_interface.RequestEnrolledNamesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4239, - serialized_end=4268, -) - - -_LOADEDKNOWNFACE = _descriptor.Descriptor( - name='LoadedKnownFace', - full_name='Anki.Vector.external_interface.LoadedKnownFace', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='seconds_since_first_enrolled', full_name='Anki.Vector.external_interface.LoadedKnownFace.seconds_since_first_enrolled', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='seconds_since_last_updated', full_name='Anki.Vector.external_interface.LoadedKnownFace.seconds_since_last_updated', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='seconds_since_last_seen', full_name='Anki.Vector.external_interface.LoadedKnownFace.seconds_since_last_seen', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='last_seen_seconds_since_epoch', full_name='Anki.Vector.external_interface.LoadedKnownFace.last_seen_seconds_since_epoch', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='face_id', full_name='Anki.Vector.external_interface.LoadedKnownFace.face_id', index=4, - number=5, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='Anki.Vector.external_interface.LoadedKnownFace.name', index=5, - number=6, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4271, - serialized_end=4465, -) - - -_ROBOTRENAMEDENROLLEDFACE = _descriptor.Descriptor( - name='RobotRenamedEnrolledFace', - full_name='Anki.Vector.external_interface.RobotRenamedEnrolledFace', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='face_id', full_name='Anki.Vector.external_interface.RobotRenamedEnrolledFace.face_id', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='Anki.Vector.external_interface.RobotRenamedEnrolledFace.name', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4467, - serialized_end=4524, -) - - -_REQUESTENROLLEDNAMESRESPONSE = _descriptor.Descriptor( - name='RequestEnrolledNamesResponse', - full_name='Anki.Vector.external_interface.RequestEnrolledNamesResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.RequestEnrolledNamesResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='faces', full_name='Anki.Vector.external_interface.RequestEnrolledNamesResponse.faces', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4527, - serialized_end=4685, -) - - -_UPDATEENROLLEDFACEBYIDREQUEST = _descriptor.Descriptor( - name='UpdateEnrolledFaceByIDRequest', - full_name='Anki.Vector.external_interface.UpdateEnrolledFaceByIDRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='face_id', full_name='Anki.Vector.external_interface.UpdateEnrolledFaceByIDRequest.face_id', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='old_name', full_name='Anki.Vector.external_interface.UpdateEnrolledFaceByIDRequest.old_name', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='new_name', full_name='Anki.Vector.external_interface.UpdateEnrolledFaceByIDRequest.new_name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4687, - serialized_end=4771, -) - - -_UPDATEENROLLEDFACEBYIDRESPONSE = _descriptor.Descriptor( - name='UpdateEnrolledFaceByIDResponse', - full_name='Anki.Vector.external_interface.UpdateEnrolledFaceByIDResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.UpdateEnrolledFaceByIDResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4773, - serialized_end=4869, -) - - -_ERASEENROLLEDFACEBYIDREQUEST = _descriptor.Descriptor( - name='EraseEnrolledFaceByIDRequest', - full_name='Anki.Vector.external_interface.EraseEnrolledFaceByIDRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='face_id', full_name='Anki.Vector.external_interface.EraseEnrolledFaceByIDRequest.face_id', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4871, - serialized_end=4918, -) - - -_ERASEENROLLEDFACEBYIDRESPONSE = _descriptor.Descriptor( - name='EraseEnrolledFaceByIDResponse', - full_name='Anki.Vector.external_interface.EraseEnrolledFaceByIDResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.EraseEnrolledFaceByIDResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4920, - serialized_end=5015, -) - - -_ERASEALLENROLLEDFACESREQUEST = _descriptor.Descriptor( - name='EraseAllEnrolledFacesRequest', - full_name='Anki.Vector.external_interface.EraseAllEnrolledFacesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5017, - serialized_end=5047, -) - - -_ERASEALLENROLLEDFACESRESPONSE = _descriptor.Descriptor( - name='EraseAllEnrolledFacesResponse', - full_name='Anki.Vector.external_interface.EraseAllEnrolledFacesResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.EraseAllEnrolledFacesResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5049, - serialized_end=5144, -) - - -_SETFACETOENROLLREQUEST = _descriptor.Descriptor( - name='SetFaceToEnrollRequest', - full_name='Anki.Vector.external_interface.SetFaceToEnrollRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='Anki.Vector.external_interface.SetFaceToEnrollRequest.name', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='observed_id', full_name='Anki.Vector.external_interface.SetFaceToEnrollRequest.observed_id', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='save_id', full_name='Anki.Vector.external_interface.SetFaceToEnrollRequest.save_id', index=2, - number=3, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='save_to_robot', full_name='Anki.Vector.external_interface.SetFaceToEnrollRequest.save_to_robot', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='say_name', full_name='Anki.Vector.external_interface.SetFaceToEnrollRequest.say_name', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='use_music', full_name='Anki.Vector.external_interface.SetFaceToEnrollRequest.use_music', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5147, - serialized_end=5283, -) - - -_SETFACETOENROLLRESPONSE = _descriptor.Descriptor( - name='SetFaceToEnrollResponse', - full_name='Anki.Vector.external_interface.SetFaceToEnrollResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.SetFaceToEnrollResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5285, - serialized_end=5374, -) - - -_DRIVEOFFCHARGERREQUEST = _descriptor.Descriptor( - name='DriveOffChargerRequest', - full_name='Anki.Vector.external_interface.DriveOffChargerRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5376, - serialized_end=5400, -) - - -_DRIVEOFFCHARGERRESPONSE = _descriptor.Descriptor( - name='DriveOffChargerResponse', - full_name='Anki.Vector.external_interface.DriveOffChargerResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DriveOffChargerResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.DriveOffChargerResponse.result', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5403, - serialized_end=5557, -) - - -_DRIVEONCHARGERREQUEST = _descriptor.Descriptor( - name='DriveOnChargerRequest', - full_name='Anki.Vector.external_interface.DriveOnChargerRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5559, - serialized_end=5582, -) - - -_DRIVEONCHARGERRESPONSE = _descriptor.Descriptor( - name='DriveOnChargerResponse', - full_name='Anki.Vector.external_interface.DriveOnChargerResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DriveOnChargerResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.DriveOnChargerResponse.result', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5585, - serialized_end=5738, -) - - -_WAKEWORDBEGIN = _descriptor.Descriptor( - name='WakeWordBegin', - full_name='Anki.Vector.external_interface.WakeWordBegin', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5740, - serialized_end=5755, -) - - -_WAKEWORDEND = _descriptor.Descriptor( - name='WakeWordEnd', - full_name='Anki.Vector.external_interface.WakeWordEnd', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='intent_heard', full_name='Anki.Vector.external_interface.WakeWordEnd.intent_heard', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='intent_json', full_name='Anki.Vector.external_interface.WakeWordEnd.intent_json', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5757, - serialized_end=5813, -) - - -_PHOTOINFO = _descriptor.Descriptor( - name='PhotoInfo', - full_name='Anki.Vector.external_interface.PhotoInfo', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='photo_id', full_name='Anki.Vector.external_interface.PhotoInfo.photo_id', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='timestamp_utc', full_name='Anki.Vector.external_interface.PhotoInfo.timestamp_utc', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='photo_copied_to_app', full_name='Anki.Vector.external_interface.PhotoInfo.photo_copied_to_app', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='thumb_copied_to_app', full_name='Anki.Vector.external_interface.PhotoInfo.thumb_copied_to_app', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5815, - serialized_end=5925, -) - - -_PHOTOSINFOREQUEST = _descriptor.Descriptor( - name='PhotosInfoRequest', - full_name='Anki.Vector.external_interface.PhotosInfoRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5927, - serialized_end=5946, -) - - -_PHOTOSINFORESPONSE = _descriptor.Descriptor( - name='PhotosInfoResponse', - full_name='Anki.Vector.external_interface.PhotosInfoResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.PhotosInfoResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='photo_infos', full_name='Anki.Vector.external_interface.PhotosInfoResponse.photo_infos', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5949, - serialized_end=6097, -) - - -_PHOTOREQUEST = _descriptor.Descriptor( - name='PhotoRequest', - full_name='Anki.Vector.external_interface.PhotoRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='photo_id', full_name='Anki.Vector.external_interface.PhotoRequest.photo_id', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6099, - serialized_end=6131, -) - - -_PHOTOPATHMESSAGE = _descriptor.Descriptor( - name='PhotoPathMessage', - full_name='Anki.Vector.external_interface.PhotoPathMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='success', full_name='Anki.Vector.external_interface.PhotoPathMessage.success', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='full_path', full_name='Anki.Vector.external_interface.PhotoPathMessage.full_path', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6133, - serialized_end=6187, -) - - -_PHOTORESPONSE = _descriptor.Descriptor( - name='PhotoResponse', - full_name='Anki.Vector.external_interface.PhotoResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.PhotoResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='success', full_name='Anki.Vector.external_interface.PhotoResponse.success', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='image', full_name='Anki.Vector.external_interface.PhotoResponse.image', index=2, - number=3, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6189, - serialized_end=6300, -) - - -_THUMBNAILREQUEST = _descriptor.Descriptor( - name='ThumbnailRequest', - full_name='Anki.Vector.external_interface.ThumbnailRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='photo_id', full_name='Anki.Vector.external_interface.ThumbnailRequest.photo_id', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6302, - serialized_end=6338, -) - - -_THUMBNAILPATHMESSAGE = _descriptor.Descriptor( - name='ThumbnailPathMessage', - full_name='Anki.Vector.external_interface.ThumbnailPathMessage', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='success', full_name='Anki.Vector.external_interface.ThumbnailPathMessage.success', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='full_path', full_name='Anki.Vector.external_interface.ThumbnailPathMessage.full_path', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6340, - serialized_end=6398, -) - - -_THUMBNAILRESPONSE = _descriptor.Descriptor( - name='ThumbnailResponse', - full_name='Anki.Vector.external_interface.ThumbnailResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.ThumbnailResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='success', full_name='Anki.Vector.external_interface.ThumbnailResponse.success', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='image', full_name='Anki.Vector.external_interface.ThumbnailResponse.image', index=2, - number=3, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6400, - serialized_end=6515, -) - - -_DELETEPHOTOREQUEST = _descriptor.Descriptor( - name='DeletePhotoRequest', - full_name='Anki.Vector.external_interface.DeletePhotoRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='photo_id', full_name='Anki.Vector.external_interface.DeletePhotoRequest.photo_id', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6517, - serialized_end=6555, -) - - -_DELETEPHOTORESPONSE = _descriptor.Descriptor( - name='DeletePhotoResponse', - full_name='Anki.Vector.external_interface.DeletePhotoResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DeletePhotoResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='success', full_name='Anki.Vector.external_interface.DeletePhotoResponse.success', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6557, - serialized_end=6659, -) - - -_PHOTOTAKEN = _descriptor.Descriptor( - name='PhotoTaken', - full_name='Anki.Vector.external_interface.PhotoTaken', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='photo_id', full_name='Anki.Vector.external_interface.PhotoTaken.photo_id', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6661, - serialized_end=6691, -) - - -_PATHMOTIONPROFILE = _descriptor.Descriptor( - name='PathMotionProfile', - full_name='Anki.Vector.external_interface.PathMotionProfile', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='speed_mmps', full_name='Anki.Vector.external_interface.PathMotionProfile.speed_mmps', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='accel_mmps2', full_name='Anki.Vector.external_interface.PathMotionProfile.accel_mmps2', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='decel_mmps2', full_name='Anki.Vector.external_interface.PathMotionProfile.decel_mmps2', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='point_turn_speed_rad_per_sec', full_name='Anki.Vector.external_interface.PathMotionProfile.point_turn_speed_rad_per_sec', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='point_turn_accel_rad_per_sec2', full_name='Anki.Vector.external_interface.PathMotionProfile.point_turn_accel_rad_per_sec2', index=4, - number=5, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='point_turn_decel_rad_per_sec2', full_name='Anki.Vector.external_interface.PathMotionProfile.point_turn_decel_rad_per_sec2', index=5, - number=6, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dock_speed_mmps', full_name='Anki.Vector.external_interface.PathMotionProfile.dock_speed_mmps', index=6, - number=7, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dock_accel_mmps2', full_name='Anki.Vector.external_interface.PathMotionProfile.dock_accel_mmps2', index=7, - number=8, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dock_decel_mmps2', full_name='Anki.Vector.external_interface.PathMotionProfile.dock_decel_mmps2', index=8, - number=9, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='reverse_speed_mmps', full_name='Anki.Vector.external_interface.PathMotionProfile.reverse_speed_mmps', index=9, - number=10, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_custom', full_name='Anki.Vector.external_interface.PathMotionProfile.is_custom', index=10, - number=11, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6694, - serialized_end=7015, -) - - -_ACTIONRESULT = _descriptor.Descriptor( - name='ActionResult', - full_name='Anki.Vector.external_interface.ActionResult', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='code', full_name='Anki.Vector.external_interface.ActionResult.code', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _ACTIONRESULT_ACTIONRESULTCODE, - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7018, - serialized_end=8674, -) - - -_GOTOPOSEREQUEST = _descriptor.Descriptor( - name='GoToPoseRequest', - full_name='Anki.Vector.external_interface.GoToPoseRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='x_mm', full_name='Anki.Vector.external_interface.GoToPoseRequest.x_mm', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='y_mm', full_name='Anki.Vector.external_interface.GoToPoseRequest.y_mm', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='rad', full_name='Anki.Vector.external_interface.GoToPoseRequest.rad', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='motion_prof', full_name='Anki.Vector.external_interface.GoToPoseRequest.motion_prof', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='id_tag', full_name='Anki.Vector.external_interface.GoToPoseRequest.id_tag', index=4, - number=5, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='num_retries', full_name='Anki.Vector.external_interface.GoToPoseRequest.num_retries', index=5, - number=6, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=8677, - serialized_end=8844, -) - - -_GOTOPOSERESPONSE = _descriptor.Descriptor( - name='GoToPoseResponse', - full_name='Anki.Vector.external_interface.GoToPoseResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.GoToPoseResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.GoToPoseResponse.result', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=8847, - serialized_end=8991, -) - - -_DOCKWITHCUBEREQUEST = _descriptor.Descriptor( - name='DockWithCubeRequest', - full_name='Anki.Vector.external_interface.DockWithCubeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='Anki.Vector.external_interface.DockWithCubeRequest.object_id', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='distance_from_marker_mm', full_name='Anki.Vector.external_interface.DockWithCubeRequest.distance_from_marker_mm', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='approach_angle_rad', full_name='Anki.Vector.external_interface.DockWithCubeRequest.approach_angle_rad', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='alignment_type', full_name='Anki.Vector.external_interface.DockWithCubeRequest.alignment_type', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='use_approach_angle', full_name='Anki.Vector.external_interface.DockWithCubeRequest.use_approach_angle', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='use_pre_dock_pose', full_name='Anki.Vector.external_interface.DockWithCubeRequest.use_pre_dock_pose', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='motion_prof', full_name='Anki.Vector.external_interface.DockWithCubeRequest.motion_prof', index=6, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='id_tag', full_name='Anki.Vector.external_interface.DockWithCubeRequest.id_tag', index=7, - number=8, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='num_retries', full_name='Anki.Vector.external_interface.DockWithCubeRequest.num_retries', index=8, - number=9, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=8994, - serialized_end=9330, -) - - -_DOCKWITHCUBERESPONSE = _descriptor.Descriptor( - name='DockWithCubeResponse', - full_name='Anki.Vector.external_interface.DockWithCubeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DockWithCubeResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.DockWithCubeResponse.result', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=9333, - serialized_end=9481, -) - - -_DRIVESTRAIGHTREQUEST = _descriptor.Descriptor( - name='DriveStraightRequest', - full_name='Anki.Vector.external_interface.DriveStraightRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='speed_mmps', full_name='Anki.Vector.external_interface.DriveStraightRequest.speed_mmps', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dist_mm', full_name='Anki.Vector.external_interface.DriveStraightRequest.dist_mm', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='should_play_animation', full_name='Anki.Vector.external_interface.DriveStraightRequest.should_play_animation', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='id_tag', full_name='Anki.Vector.external_interface.DriveStraightRequest.id_tag', index=3, - number=4, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='num_retries', full_name='Anki.Vector.external_interface.DriveStraightRequest.num_retries', index=4, - number=5, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=9483, - serialized_end=9610, -) - - -_DRIVESTRAIGHTRESPONSE = _descriptor.Descriptor( - name='DriveStraightResponse', - full_name='Anki.Vector.external_interface.DriveStraightResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.DriveStraightResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.DriveStraightResponse.result', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=9613, - serialized_end=9762, -) - - -_TURNINPLACEREQUEST = _descriptor.Descriptor( - name='TurnInPlaceRequest', - full_name='Anki.Vector.external_interface.TurnInPlaceRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='angle_rad', full_name='Anki.Vector.external_interface.TurnInPlaceRequest.angle_rad', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='speed_rad_per_sec', full_name='Anki.Vector.external_interface.TurnInPlaceRequest.speed_rad_per_sec', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='accel_rad_per_sec2', full_name='Anki.Vector.external_interface.TurnInPlaceRequest.accel_rad_per_sec2', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tol_rad', full_name='Anki.Vector.external_interface.TurnInPlaceRequest.tol_rad', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_absolute', full_name='Anki.Vector.external_interface.TurnInPlaceRequest.is_absolute', index=4, - number=5, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='id_tag', full_name='Anki.Vector.external_interface.TurnInPlaceRequest.id_tag', index=5, - number=6, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='num_retries', full_name='Anki.Vector.external_interface.TurnInPlaceRequest.num_retries', index=6, - number=7, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=9765, - serialized_end=9934, -) - - -_TURNINPLACERESPONSE = _descriptor.Descriptor( - name='TurnInPlaceResponse', - full_name='Anki.Vector.external_interface.TurnInPlaceResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.TurnInPlaceResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.TurnInPlaceResponse.result', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=9937, - serialized_end=10084, -) - - -_SETHEADANGLEREQUEST = _descriptor.Descriptor( - name='SetHeadAngleRequest', - full_name='Anki.Vector.external_interface.SetHeadAngleRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='angle_rad', full_name='Anki.Vector.external_interface.SetHeadAngleRequest.angle_rad', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_speed_rad_per_sec', full_name='Anki.Vector.external_interface.SetHeadAngleRequest.max_speed_rad_per_sec', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='accel_rad_per_sec2', full_name='Anki.Vector.external_interface.SetHeadAngleRequest.accel_rad_per_sec2', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='duration_sec', full_name='Anki.Vector.external_interface.SetHeadAngleRequest.duration_sec', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='id_tag', full_name='Anki.Vector.external_interface.SetHeadAngleRequest.id_tag', index=4, - number=5, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='num_retries', full_name='Anki.Vector.external_interface.SetHeadAngleRequest.num_retries', index=5, - number=6, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=10087, - serialized_end=10245, -) - - -_SETHEADANGLERESPONSE = _descriptor.Descriptor( - name='SetHeadAngleResponse', - full_name='Anki.Vector.external_interface.SetHeadAngleResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.SetHeadAngleResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.SetHeadAngleResponse.result', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=10248, - serialized_end=10396, -) - - -_SETLIFTHEIGHTREQUEST = _descriptor.Descriptor( - name='SetLiftHeightRequest', - full_name='Anki.Vector.external_interface.SetLiftHeightRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='height_mm', full_name='Anki.Vector.external_interface.SetLiftHeightRequest.height_mm', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_speed_rad_per_sec', full_name='Anki.Vector.external_interface.SetLiftHeightRequest.max_speed_rad_per_sec', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='accel_rad_per_sec2', full_name='Anki.Vector.external_interface.SetLiftHeightRequest.accel_rad_per_sec2', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='duration_sec', full_name='Anki.Vector.external_interface.SetLiftHeightRequest.duration_sec', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='id_tag', full_name='Anki.Vector.external_interface.SetLiftHeightRequest.id_tag', index=4, - number=5, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='num_retries', full_name='Anki.Vector.external_interface.SetLiftHeightRequest.num_retries', index=5, - number=6, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=10399, - serialized_end=10558, -) - - -_SETLIFTHEIGHTRESPONSE = _descriptor.Descriptor( - name='SetLiftHeightResponse', - full_name='Anki.Vector.external_interface.SetLiftHeightResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.SetLiftHeightResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.SetLiftHeightResponse.result', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=10561, - serialized_end=10710, -) - - -_NETWORKSTATS = _descriptor.Descriptor( - name='NetworkStats', - full_name='Anki.Vector.external_interface.NetworkStats', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='g_net_stat1_num_connections', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stat1_num_connections', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_stat2_latency_avg', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stat2_latency_avg', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_stat3_latency_sd', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stat3_latency_sd', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_stat4_latency_min', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stat4_latency_min', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_stat5_latency_max', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stat5_latency_max', index=4, - number=5, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_stat6_ping_arrived_pc', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stat6_ping_arrived_pc', index=5, - number=6, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_stat7_ext_queued_avg_ms', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stat7_ext_queued_avg_ms', index=6, - number=7, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_stat8_ext_queued_min_ms', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stat8_ext_queued_min_ms', index=7, - number=8, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_stat9_ext_queued_max_ms', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stat9_ext_queued_max_ms', index=8, - number=9, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_stata_queued_avg_ms', full_name='Anki.Vector.external_interface.NetworkStats.g_net_stata_queued_avg_ms', index=9, - number=10, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_statb_queued_min_ms', full_name='Anki.Vector.external_interface.NetworkStats.g_net_statb_queued_min_ms', index=10, - number=11, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='g_net_statc_queued_max_ms', full_name='Anki.Vector.external_interface.NetworkStats.g_net_statc_queued_max_ms', index=11, - number=12, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=10713, - serialized_end=11154, -) - - -_BATTERYSTATEREQUEST = _descriptor.Descriptor( - name='BatteryStateRequest', - full_name='Anki.Vector.external_interface.BatteryStateRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=11156, - serialized_end=11177, -) - - -_BATTERYSTATERESPONSE = _descriptor.Descriptor( - name='BatteryStateResponse', - full_name='Anki.Vector.external_interface.BatteryStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.BatteryStateResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='battery_level', full_name='Anki.Vector.external_interface.BatteryStateResponse.battery_level', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='battery_volts', full_name='Anki.Vector.external_interface.BatteryStateResponse.battery_volts', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_charging', full_name='Anki.Vector.external_interface.BatteryStateResponse.is_charging', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_on_charger_platform', full_name='Anki.Vector.external_interface.BatteryStateResponse.is_on_charger_platform', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='suggested_charger_sec', full_name='Anki.Vector.external_interface.BatteryStateResponse.suggested_charger_sec', index=5, - number=6, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cube_battery', full_name='Anki.Vector.external_interface.BatteryStateResponse.cube_battery', index=6, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=11180, - serialized_end=11509, -) - - -_CUBEBATTERY = _descriptor.Descriptor( - name='CubeBattery', - full_name='Anki.Vector.external_interface.CubeBattery', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='level', full_name='Anki.Vector.external_interface.CubeBattery.level', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='factory_id', full_name='Anki.Vector.external_interface.CubeBattery.factory_id', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='battery_volts', full_name='Anki.Vector.external_interface.CubeBattery.battery_volts', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='time_since_last_reading_sec', full_name='Anki.Vector.external_interface.CubeBattery.time_since_last_reading_sec', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _CUBEBATTERY_CUBEBATTERYLEVEL, - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=11512, - serialized_end=11723, -) - - -_VERSIONSTATEREQUEST = _descriptor.Descriptor( - name='VersionStateRequest', - full_name='Anki.Vector.external_interface.VersionStateRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=11725, - serialized_end=11746, -) - - -_VERSIONSTATERESPONSE = _descriptor.Descriptor( - name='VersionStateResponse', - full_name='Anki.Vector.external_interface.VersionStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.VersionStateResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='os_version', full_name='Anki.Vector.external_interface.VersionStateResponse.os_version', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='engine_build_id', full_name='Anki.Vector.external_interface.VersionStateResponse.engine_build_id', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=11749, - serialized_end=11880, -) - - -_NETWORKSTATEREQUEST = _descriptor.Descriptor( - name='NetworkStateRequest', - full_name='Anki.Vector.external_interface.NetworkStateRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=11882, - serialized_end=11903, -) - - -_NETWORKSTATERESPONSE = _descriptor.Descriptor( - name='NetworkStateResponse', - full_name='Anki.Vector.external_interface.NetworkStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.NetworkStateResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='network_stats', full_name='Anki.Vector.external_interface.NetworkStateResponse.network_stats', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=11906, - serialized_end=12061, -) - - -_SAYTEXTREQUEST = _descriptor.Descriptor( - name='SayTextRequest', - full_name='Anki.Vector.external_interface.SayTextRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='text', full_name='Anki.Vector.external_interface.SayTextRequest.text', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='use_vector_voice', full_name='Anki.Vector.external_interface.SayTextRequest.use_vector_voice', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='duration_scalar', full_name='Anki.Vector.external_interface.SayTextRequest.duration_scalar', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=12063, - serialized_end=12144, -) - - -_SAYTEXTRESPONSE = _descriptor.Descriptor( - name='SayTextResponse', - full_name='Anki.Vector.external_interface.SayTextResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.SayTextResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='Anki.Vector.external_interface.SayTextResponse.state', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _SAYTEXTRESPONSE_UTTERANCESTATE, - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=12147, - serialized_end=12392, -) - - -_STIMULATIONINFO = _descriptor.Descriptor( - name='StimulationInfo', - full_name='Anki.Vector.external_interface.StimulationInfo', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='emotion_events', full_name='Anki.Vector.external_interface.StimulationInfo.emotion_events', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='Anki.Vector.external_interface.StimulationInfo.value', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='velocity', full_name='Anki.Vector.external_interface.StimulationInfo.velocity', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='accel', full_name='Anki.Vector.external_interface.StimulationInfo.accel', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value_before_event', full_name='Anki.Vector.external_interface.StimulationInfo.value_before_event', index=4, - number=5, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='min_value', full_name='Anki.Vector.external_interface.StimulationInfo.min_value', index=5, - number=6, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_value', full_name='Anki.Vector.external_interface.StimulationInfo.max_value', index=6, - number=7, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=12395, - serialized_end=12550, -) - - -_AUDIOSENDMODEREQUEST = _descriptor.Descriptor( - name='AudioSendModeRequest', - full_name='Anki.Vector.external_interface.AudioSendModeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='mode', full_name='Anki.Vector.external_interface.AudioSendModeRequest.mode', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=12552, - serialized_end=12641, -) - - -_AUDIOSENDMODECHANGED = _descriptor.Descriptor( - name='AudioSendModeChanged', - full_name='Anki.Vector.external_interface.AudioSendModeChanged', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='mode', full_name='Anki.Vector.external_interface.AudioSendModeChanged.mode', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=12643, - serialized_end=12732, -) - - -_AUDIOCHUNK = _descriptor.Descriptor( - name='AudioChunk', - full_name='Anki.Vector.external_interface.AudioChunk', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='robot_time_stamp', full_name='Anki.Vector.external_interface.AudioChunk.robot_time_stamp', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='group_id', full_name='Anki.Vector.external_interface.AudioChunk.group_id', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='chunk_id', full_name='Anki.Vector.external_interface.AudioChunk.chunk_id', index=2, - number=3, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='audio_chunk_count', full_name='Anki.Vector.external_interface.AudioChunk.audio_chunk_count', index=3, - number=4, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='signal_power', full_name='Anki.Vector.external_interface.AudioChunk.signal_power', index=4, - number=5, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='direction_strengths', full_name='Anki.Vector.external_interface.AudioChunk.direction_strengths', index=5, - number=6, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_direction', full_name='Anki.Vector.external_interface.AudioChunk.source_direction', index=6, - number=7, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_confidence', full_name='Anki.Vector.external_interface.AudioChunk.source_confidence', index=7, - number=8, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='noise_floor_power', full_name='Anki.Vector.external_interface.AudioChunk.noise_floor_power', index=8, - number=9, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=12735, - serialized_end=12967, -) - - -_AUDIOFEEDREQUEST = _descriptor.Descriptor( - name='AudioFeedRequest', - full_name='Anki.Vector.external_interface.AudioFeedRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=12969, - serialized_end=12987, -) - - -_AUDIOFEEDRESPONSE = _descriptor.Descriptor( - name='AudioFeedResponse', - full_name='Anki.Vector.external_interface.AudioFeedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='robot_time_stamp', full_name='Anki.Vector.external_interface.AudioFeedResponse.robot_time_stamp', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='group_id', full_name='Anki.Vector.external_interface.AudioFeedResponse.group_id', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='signal_power', full_name='Anki.Vector.external_interface.AudioFeedResponse.signal_power', index=2, - number=3, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='direction_strengths', full_name='Anki.Vector.external_interface.AudioFeedResponse.direction_strengths', index=3, - number=4, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_direction', full_name='Anki.Vector.external_interface.AudioFeedResponse.source_direction', index=4, - number=5, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_confidence', full_name='Anki.Vector.external_interface.AudioFeedResponse.source_confidence', index=5, - number=6, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='noise_floor_power', full_name='Anki.Vector.external_interface.AudioFeedResponse.noise_floor_power', index=6, - number=7, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\200\246\035\001')), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=12990, - serialized_end=13190, -) - - -_ENABLEMARKERDETECTIONREQUEST = _descriptor.Descriptor( - name='EnableMarkerDetectionRequest', - full_name='Anki.Vector.external_interface.EnableMarkerDetectionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='enable', full_name='Anki.Vector.external_interface.EnableMarkerDetectionRequest.enable', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13192, - serialized_end=13238, -) - - -_ENABLEMARKERDETECTIONRESPONSE = _descriptor.Descriptor( - name='EnableMarkerDetectionResponse', - full_name='Anki.Vector.external_interface.EnableMarkerDetectionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.EnableMarkerDetectionResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13240, - serialized_end=13335, -) - - -_ENABLEFACEDETECTIONREQUEST = _descriptor.Descriptor( - name='EnableFaceDetectionRequest', - full_name='Anki.Vector.external_interface.EnableFaceDetectionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='enable', full_name='Anki.Vector.external_interface.EnableFaceDetectionRequest.enable', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='enable_smile_detection', full_name='Anki.Vector.external_interface.EnableFaceDetectionRequest.enable_smile_detection', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='enable_expression_estimation', full_name='Anki.Vector.external_interface.EnableFaceDetectionRequest.enable_expression_estimation', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='enable_blink_detection', full_name='Anki.Vector.external_interface.EnableFaceDetectionRequest.enable_blink_detection', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='enable_gaze_detection', full_name='Anki.Vector.external_interface.EnableFaceDetectionRequest.enable_gaze_detection', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13338, - serialized_end=13515, -) - - -_ENABLEFACEDETECTIONRESPONSE = _descriptor.Descriptor( - name='EnableFaceDetectionResponse', - full_name='Anki.Vector.external_interface.EnableFaceDetectionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.EnableFaceDetectionResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13517, - serialized_end=13610, -) - - -_ENABLEMOTIONDETECTIONREQUEST = _descriptor.Descriptor( - name='EnableMotionDetectionRequest', - full_name='Anki.Vector.external_interface.EnableMotionDetectionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='enable', full_name='Anki.Vector.external_interface.EnableMotionDetectionRequest.enable', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13612, - serialized_end=13658, -) - - -_ENABLEMOTIONDETECTIONRESPONSE = _descriptor.Descriptor( - name='EnableMotionDetectionResponse', - full_name='Anki.Vector.external_interface.EnableMotionDetectionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.EnableMotionDetectionResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13660, - serialized_end=13755, -) - - -_ENABLEMIRRORMODEREQUEST = _descriptor.Descriptor( - name='EnableMirrorModeRequest', - full_name='Anki.Vector.external_interface.EnableMirrorModeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='enable', full_name='Anki.Vector.external_interface.EnableMirrorModeRequest.enable', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13757, - serialized_end=13798, -) - - -_ENABLEMIRRORMODERESPONSE = _descriptor.Descriptor( - name='EnableMirrorModeResponse', - full_name='Anki.Vector.external_interface.EnableMirrorModeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.EnableMirrorModeResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13800, - serialized_end=13890, -) - - -_MIRRORMODEDISABLED = _descriptor.Descriptor( - name='MirrorModeDisabled', - full_name='Anki.Vector.external_interface.MirrorModeDisabled', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13892, - serialized_end=13912, -) - - -_ENABLEIMAGESTREAMINGREQUEST = _descriptor.Descriptor( - name='EnableImageStreamingRequest', - full_name='Anki.Vector.external_interface.EnableImageStreamingRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='enable', full_name='Anki.Vector.external_interface.EnableImageStreamingRequest.enable', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13914, - serialized_end=13959, -) - - -_ENABLEIMAGESTREAMINGRESPONSE = _descriptor.Descriptor( - name='EnableImageStreamingResponse', - full_name='Anki.Vector.external_interface.EnableImageStreamingResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.EnableImageStreamingResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13961, - serialized_end=14055, -) - - -_VISIONMODESAUTODISABLED = _descriptor.Descriptor( - name='VisionModesAutoDisabled', - full_name='Anki.Vector.external_interface.VisionModesAutoDisabled', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=14057, - serialized_end=14082, -) - - -_IMAGECHUNK = _descriptor.Descriptor( - name='ImageChunk', - full_name='Anki.Vector.external_interface.ImageChunk', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='frame_time_stamp', full_name='Anki.Vector.external_interface.ImageChunk.frame_time_stamp', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='image_id', full_name='Anki.Vector.external_interface.ImageChunk.image_id', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='width', full_name='Anki.Vector.external_interface.ImageChunk.width', index=2, - number=3, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='height', full_name='Anki.Vector.external_interface.ImageChunk.height', index=3, - number=4, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='image_encoding', full_name='Anki.Vector.external_interface.ImageChunk.image_encoding', index=4, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='display_index', full_name='Anki.Vector.external_interface.ImageChunk.display_index', index=5, - number=6, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='image_chunk_count', full_name='Anki.Vector.external_interface.ImageChunk.image_chunk_count', index=6, - number=7, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='chunk_id', full_name='Anki.Vector.external_interface.ImageChunk.chunk_id', index=7, - number=8, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='data', full_name='Anki.Vector.external_interface.ImageChunk.data', index=8, - number=9, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _IMAGECHUNK_IMAGEENCODING, - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=14085, - serialized_end=14550, -) - - -_CAMERAFEEDREQUEST = _descriptor.Descriptor( - name='CameraFeedRequest', - full_name='Anki.Vector.external_interface.CameraFeedRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=14552, - serialized_end=14571, -) - - -_CAMERAFEEDRESPONSE = _descriptor.Descriptor( - name='CameraFeedResponse', - full_name='Anki.Vector.external_interface.CameraFeedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='frame_time_stamp', full_name='Anki.Vector.external_interface.CameraFeedResponse.frame_time_stamp', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='image_id', full_name='Anki.Vector.external_interface.CameraFeedResponse.image_id', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='image_encoding', full_name='Anki.Vector.external_interface.CameraFeedResponse.image_encoding', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='data', full_name='Anki.Vector.external_interface.CameraFeedResponse.data', index=3, - number=4, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\200\246\035\001')), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=14574, - serialized_end=14740, -) - - -_SETEYECOLORREQUEST = _descriptor.Descriptor( - name='SetEyeColorRequest', - full_name='Anki.Vector.external_interface.SetEyeColorRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='hue', full_name='Anki.Vector.external_interface.SetEyeColorRequest.hue', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='saturation', full_name='Anki.Vector.external_interface.SetEyeColorRequest.saturation', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=14742, - serialized_end=14795, -) - - -_SETEYECOLORRESPONSE = _descriptor.Descriptor( - name='SetEyeColorResponse', - full_name='Anki.Vector.external_interface.SetEyeColorResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.SetEyeColorResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=14797, - serialized_end=14882, -) - - -_SDKINITIALIZATIONREQUEST = _descriptor.Descriptor( - name='SDKInitializationRequest', - full_name='Anki.Vector.external_interface.SDKInitializationRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='sdk_module_version', full_name='Anki.Vector.external_interface.SDKInitializationRequest.sdk_module_version', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='python_version', full_name='Anki.Vector.external_interface.SDKInitializationRequest.python_version', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='python_implementation', full_name='Anki.Vector.external_interface.SDKInitializationRequest.python_implementation', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='os_version', full_name='Anki.Vector.external_interface.SDKInitializationRequest.os_version', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cpu_version', full_name='Anki.Vector.external_interface.SDKInitializationRequest.cpu_version', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=14885, - serialized_end=15035, -) - - -_SDKINITIALIZATIONRESPONSE = _descriptor.Descriptor( - name='SDKInitializationResponse', - full_name='Anki.Vector.external_interface.SDKInitializationResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.SDKInitializationResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=15037, - serialized_end=15128, -) - -_DRIVEWHEELSRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_MOVEHEADRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_MOVELIFTRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_PLAYANIMATIONREQUEST.fields_by_name['animation'].message_type = _ANIMATION -_PLAYANIMATIONRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_PLAYANIMATIONRESPONSE.fields_by_name['result'].enum_type = _BEHAVIORRESULTS -_LISTANIMATIONSRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_LISTANIMATIONSRESPONSE.fields_by_name['animation_names'].message_type = _ANIMATION -_DISPLAYFACEIMAGERGBRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_STATUS.fields_by_name['meet_victor_face_scan_started'].message_type = _MEETVICTORFACESCANSTARTED -_STATUS.fields_by_name['meet_victor_face_scan_complete'].message_type = _MEETVICTORFACESCANCOMPLETE -_STATUS.fields_by_name['face_enrollment_completed'].message_type = _FACEENROLLMENTCOMPLETED -_STATUS.oneofs_by_name['status_type'].fields.append( - _STATUS.fields_by_name['meet_victor_face_scan_started']) -_STATUS.fields_by_name['meet_victor_face_scan_started'].containing_oneof = _STATUS.oneofs_by_name['status_type'] -_STATUS.oneofs_by_name['status_type'].fields.append( - _STATUS.fields_by_name['meet_victor_face_scan_complete']) -_STATUS.fields_by_name['meet_victor_face_scan_complete'].containing_oneof = _STATUS.oneofs_by_name['status_type'] -_STATUS.oneofs_by_name['status_type'].fields.append( - _STATUS.fields_by_name['face_enrollment_completed']) -_STATUS.fields_by_name['face_enrollment_completed'].containing_oneof = _STATUS.oneofs_by_name['status_type'] -_WAKEWORD.fields_by_name['wake_word_begin'].message_type = _WAKEWORDBEGIN -_WAKEWORD.fields_by_name['wake_word_end'].message_type = _WAKEWORDEND -_WAKEWORD.oneofs_by_name['wake_word_type'].fields.append( - _WAKEWORD.fields_by_name['wake_word_begin']) -_WAKEWORD.fields_by_name['wake_word_begin'].containing_oneof = _WAKEWORD.oneofs_by_name['wake_word_type'] -_WAKEWORD.oneofs_by_name['wake_word_type'].fields.append( - _WAKEWORD.fields_by_name['wake_word_end']) -_WAKEWORD.fields_by_name['wake_word_end'].containing_oneof = _WAKEWORD.oneofs_by_name['wake_word_type'] -_TIMESTAMPEDSTATUS.fields_by_name['status'].message_type = _STATUS -_ROBOTSTATE.fields_by_name['pose'].message_type = _POSESTRUCT -_ROBOTSTATE.fields_by_name['accel'].message_type = _ACCELDATA -_ROBOTSTATE.fields_by_name['gyro'].message_type = _GYRODATA -_ROBOTSTATE.fields_by_name['prox_data'].message_type = _PROXDATA -_ROBOTSTATE.fields_by_name['touch_data'].message_type = _TOUCHDATA -_ROBOTOBSERVEDFACE.fields_by_name['pose'].message_type = _POSESTRUCT -_ROBOTOBSERVEDFACE.fields_by_name['img_rect'].message_type = _CLADRECT -_ROBOTOBSERVEDFACE.fields_by_name['expression'].enum_type = _FACIALEXPRESSION -_ROBOTOBSERVEDFACE.fields_by_name['left_eye'].message_type = _CLADPOINT -_ROBOTOBSERVEDFACE.fields_by_name['right_eye'].message_type = _CLADPOINT -_ROBOTOBSERVEDFACE.fields_by_name['nose'].message_type = _CLADPOINT -_ROBOTOBSERVEDFACE.fields_by_name['mouth'].message_type = _CLADPOINT -_APPINTENTRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_FACEENROLLMENTCOMPLETED.fields_by_name['result'].enum_type = _FACEENROLLMENTRESULT -_CANCELFACEENROLLMENTRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_REQUESTENROLLEDNAMESRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_REQUESTENROLLEDNAMESRESPONSE.fields_by_name['faces'].message_type = _LOADEDKNOWNFACE -_UPDATEENROLLEDFACEBYIDRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_ERASEENROLLEDFACEBYIDRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_ERASEALLENROLLEDFACESRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_SETFACETOENROLLRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_DRIVEOFFCHARGERRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_DRIVEOFFCHARGERRESPONSE.fields_by_name['result'].enum_type = _BEHAVIORRESULTS -_DRIVEONCHARGERRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_DRIVEONCHARGERRESPONSE.fields_by_name['result'].enum_type = _BEHAVIORRESULTS -_PHOTOSINFORESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_PHOTOSINFORESPONSE.fields_by_name['photo_infos'].message_type = _PHOTOINFO -_PHOTORESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_THUMBNAILRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_DELETEPHOTORESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_ACTIONRESULT.fields_by_name['code'].enum_type = _ACTIONRESULT_ACTIONRESULTCODE -_ACTIONRESULT_ACTIONRESULTCODE.containing_type = _ACTIONRESULT -_GOTOPOSEREQUEST.fields_by_name['motion_prof'].message_type = _PATHMOTIONPROFILE -_GOTOPOSERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_GOTOPOSERESPONSE.fields_by_name['result'].message_type = _ACTIONRESULT -_DOCKWITHCUBEREQUEST.fields_by_name['alignment_type'].enum_type = _ALIGNMENTTYPE -_DOCKWITHCUBEREQUEST.fields_by_name['motion_prof'].message_type = _PATHMOTIONPROFILE -_DOCKWITHCUBERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_DOCKWITHCUBERESPONSE.fields_by_name['result'].message_type = _ACTIONRESULT -_DRIVESTRAIGHTRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_DRIVESTRAIGHTRESPONSE.fields_by_name['result'].message_type = _ACTIONRESULT -_TURNINPLACERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_TURNINPLACERESPONSE.fields_by_name['result'].message_type = _ACTIONRESULT -_SETHEADANGLERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_SETHEADANGLERESPONSE.fields_by_name['result'].message_type = _ACTIONRESULT -_SETLIFTHEIGHTRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_SETLIFTHEIGHTRESPONSE.fields_by_name['result'].message_type = _ACTIONRESULT -_BATTERYSTATERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_BATTERYSTATERESPONSE.fields_by_name['battery_level'].enum_type = _BATTERYLEVEL -_BATTERYSTATERESPONSE.fields_by_name['cube_battery'].message_type = _CUBEBATTERY -_CUBEBATTERY.fields_by_name['level'].enum_type = _CUBEBATTERY_CUBEBATTERYLEVEL -_CUBEBATTERY_CUBEBATTERYLEVEL.containing_type = _CUBEBATTERY -_VERSIONSTATERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_NETWORKSTATERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_NETWORKSTATERESPONSE.fields_by_name['network_stats'].message_type = _NETWORKSTATS -_SAYTEXTRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_SAYTEXTRESPONSE.fields_by_name['state'].enum_type = _SAYTEXTRESPONSE_UTTERANCESTATE -_SAYTEXTRESPONSE_UTTERANCESTATE.containing_type = _SAYTEXTRESPONSE -_AUDIOSENDMODEREQUEST.fields_by_name['mode'].enum_type = _AUDIOPROCESSINGMODE -_AUDIOSENDMODECHANGED.fields_by_name['mode'].enum_type = _AUDIOPROCESSINGMODE -_ENABLEMARKERDETECTIONRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_ENABLEFACEDETECTIONRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_ENABLEMOTIONDETECTIONRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_ENABLEMIRRORMODERESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_ENABLEIMAGESTREAMINGRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_IMAGECHUNK.fields_by_name['image_encoding'].enum_type = _IMAGECHUNK_IMAGEENCODING -_IMAGECHUNK_IMAGEENCODING.containing_type = _IMAGECHUNK -_CAMERAFEEDRESPONSE.fields_by_name['image_encoding'].enum_type = _IMAGECHUNK_IMAGEENCODING -_SETEYECOLORRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_SDKINITIALIZATIONRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -DESCRIPTOR.message_types_by_name['KeepAlivePing'] = _KEEPALIVEPING -DESCRIPTOR.message_types_by_name['Animation'] = _ANIMATION -DESCRIPTOR.message_types_by_name['DriveWheelsRequest'] = _DRIVEWHEELSREQUEST -DESCRIPTOR.message_types_by_name['DriveWheelsResponse'] = _DRIVEWHEELSRESPONSE -DESCRIPTOR.message_types_by_name['MoveHeadRequest'] = _MOVEHEADREQUEST -DESCRIPTOR.message_types_by_name['MoveHeadResponse'] = _MOVEHEADRESPONSE -DESCRIPTOR.message_types_by_name['MoveLiftRequest'] = _MOVELIFTREQUEST -DESCRIPTOR.message_types_by_name['MoveLiftResponse'] = _MOVELIFTRESPONSE -DESCRIPTOR.message_types_by_name['PlayAnimationRequest'] = _PLAYANIMATIONREQUEST -DESCRIPTOR.message_types_by_name['PlayAnimationResponse'] = _PLAYANIMATIONRESPONSE -DESCRIPTOR.message_types_by_name['ListAnimationsRequest'] = _LISTANIMATIONSREQUEST -DESCRIPTOR.message_types_by_name['ListAnimationsResponse'] = _LISTANIMATIONSRESPONSE -DESCRIPTOR.message_types_by_name['DisplayFaceImageRGBRequest'] = _DISPLAYFACEIMAGERGBREQUEST -DESCRIPTOR.message_types_by_name['DisplayFaceImageRGBResponse'] = _DISPLAYFACEIMAGERGBRESPONSE -DESCRIPTOR.message_types_by_name['MeetVictorFaceScanStarted'] = _MEETVICTORFACESCANSTARTED -DESCRIPTOR.message_types_by_name['MeetVictorFaceScanComplete'] = _MEETVICTORFACESCANCOMPLETE -DESCRIPTOR.message_types_by_name['Status'] = _STATUS -DESCRIPTOR.message_types_by_name['WakeWord'] = _WAKEWORD -DESCRIPTOR.message_types_by_name['TimeStampedStatus'] = _TIMESTAMPEDSTATUS -DESCRIPTOR.message_types_by_name['PoseStruct'] = _POSESTRUCT -DESCRIPTOR.message_types_by_name['AccelData'] = _ACCELDATA -DESCRIPTOR.message_types_by_name['GyroData'] = _GYRODATA -DESCRIPTOR.message_types_by_name['ProxData'] = _PROXDATA -DESCRIPTOR.message_types_by_name['TouchData'] = _TOUCHDATA -DESCRIPTOR.message_types_by_name['RobotState'] = _ROBOTSTATE -DESCRIPTOR.message_types_by_name['CladPoint'] = _CLADPOINT -DESCRIPTOR.message_types_by_name['CladRect'] = _CLADRECT -DESCRIPTOR.message_types_by_name['RobotObservedFace'] = _ROBOTOBSERVEDFACE -DESCRIPTOR.message_types_by_name['RobotChangedObservedFaceID'] = _ROBOTCHANGEDOBSERVEDFACEID -DESCRIPTOR.message_types_by_name['AppIntentRequest'] = _APPINTENTREQUEST -DESCRIPTOR.message_types_by_name['AppDisconnected'] = _APPDISCONNECTED -DESCRIPTOR.message_types_by_name['AppIntentResponse'] = _APPINTENTRESPONSE -DESCRIPTOR.message_types_by_name['FaceEnrollmentCompleted'] = _FACEENROLLMENTCOMPLETED -DESCRIPTOR.message_types_by_name['CancelFaceEnrollmentRequest'] = _CANCELFACEENROLLMENTREQUEST -DESCRIPTOR.message_types_by_name['CancelFaceEnrollmentResponse'] = _CANCELFACEENROLLMENTRESPONSE -DESCRIPTOR.message_types_by_name['RequestEnrolledNamesRequest'] = _REQUESTENROLLEDNAMESREQUEST -DESCRIPTOR.message_types_by_name['LoadedKnownFace'] = _LOADEDKNOWNFACE -DESCRIPTOR.message_types_by_name['RobotRenamedEnrolledFace'] = _ROBOTRENAMEDENROLLEDFACE -DESCRIPTOR.message_types_by_name['RequestEnrolledNamesResponse'] = _REQUESTENROLLEDNAMESRESPONSE -DESCRIPTOR.message_types_by_name['UpdateEnrolledFaceByIDRequest'] = _UPDATEENROLLEDFACEBYIDREQUEST -DESCRIPTOR.message_types_by_name['UpdateEnrolledFaceByIDResponse'] = _UPDATEENROLLEDFACEBYIDRESPONSE -DESCRIPTOR.message_types_by_name['EraseEnrolledFaceByIDRequest'] = _ERASEENROLLEDFACEBYIDREQUEST -DESCRIPTOR.message_types_by_name['EraseEnrolledFaceByIDResponse'] = _ERASEENROLLEDFACEBYIDRESPONSE -DESCRIPTOR.message_types_by_name['EraseAllEnrolledFacesRequest'] = _ERASEALLENROLLEDFACESREQUEST -DESCRIPTOR.message_types_by_name['EraseAllEnrolledFacesResponse'] = _ERASEALLENROLLEDFACESRESPONSE -DESCRIPTOR.message_types_by_name['SetFaceToEnrollRequest'] = _SETFACETOENROLLREQUEST -DESCRIPTOR.message_types_by_name['SetFaceToEnrollResponse'] = _SETFACETOENROLLRESPONSE -DESCRIPTOR.message_types_by_name['DriveOffChargerRequest'] = _DRIVEOFFCHARGERREQUEST -DESCRIPTOR.message_types_by_name['DriveOffChargerResponse'] = _DRIVEOFFCHARGERRESPONSE -DESCRIPTOR.message_types_by_name['DriveOnChargerRequest'] = _DRIVEONCHARGERREQUEST -DESCRIPTOR.message_types_by_name['DriveOnChargerResponse'] = _DRIVEONCHARGERRESPONSE -DESCRIPTOR.message_types_by_name['WakeWordBegin'] = _WAKEWORDBEGIN -DESCRIPTOR.message_types_by_name['WakeWordEnd'] = _WAKEWORDEND -DESCRIPTOR.message_types_by_name['PhotoInfo'] = _PHOTOINFO -DESCRIPTOR.message_types_by_name['PhotosInfoRequest'] = _PHOTOSINFOREQUEST -DESCRIPTOR.message_types_by_name['PhotosInfoResponse'] = _PHOTOSINFORESPONSE -DESCRIPTOR.message_types_by_name['PhotoRequest'] = _PHOTOREQUEST -DESCRIPTOR.message_types_by_name['PhotoPathMessage'] = _PHOTOPATHMESSAGE -DESCRIPTOR.message_types_by_name['PhotoResponse'] = _PHOTORESPONSE -DESCRIPTOR.message_types_by_name['ThumbnailRequest'] = _THUMBNAILREQUEST -DESCRIPTOR.message_types_by_name['ThumbnailPathMessage'] = _THUMBNAILPATHMESSAGE -DESCRIPTOR.message_types_by_name['ThumbnailResponse'] = _THUMBNAILRESPONSE -DESCRIPTOR.message_types_by_name['DeletePhotoRequest'] = _DELETEPHOTOREQUEST -DESCRIPTOR.message_types_by_name['DeletePhotoResponse'] = _DELETEPHOTORESPONSE -DESCRIPTOR.message_types_by_name['PhotoTaken'] = _PHOTOTAKEN -DESCRIPTOR.message_types_by_name['PathMotionProfile'] = _PATHMOTIONPROFILE -DESCRIPTOR.message_types_by_name['ActionResult'] = _ACTIONRESULT -DESCRIPTOR.message_types_by_name['GoToPoseRequest'] = _GOTOPOSEREQUEST -DESCRIPTOR.message_types_by_name['GoToPoseResponse'] = _GOTOPOSERESPONSE -DESCRIPTOR.message_types_by_name['DockWithCubeRequest'] = _DOCKWITHCUBEREQUEST -DESCRIPTOR.message_types_by_name['DockWithCubeResponse'] = _DOCKWITHCUBERESPONSE -DESCRIPTOR.message_types_by_name['DriveStraightRequest'] = _DRIVESTRAIGHTREQUEST -DESCRIPTOR.message_types_by_name['DriveStraightResponse'] = _DRIVESTRAIGHTRESPONSE -DESCRIPTOR.message_types_by_name['TurnInPlaceRequest'] = _TURNINPLACEREQUEST -DESCRIPTOR.message_types_by_name['TurnInPlaceResponse'] = _TURNINPLACERESPONSE -DESCRIPTOR.message_types_by_name['SetHeadAngleRequest'] = _SETHEADANGLEREQUEST -DESCRIPTOR.message_types_by_name['SetHeadAngleResponse'] = _SETHEADANGLERESPONSE -DESCRIPTOR.message_types_by_name['SetLiftHeightRequest'] = _SETLIFTHEIGHTREQUEST -DESCRIPTOR.message_types_by_name['SetLiftHeightResponse'] = _SETLIFTHEIGHTRESPONSE -DESCRIPTOR.message_types_by_name['NetworkStats'] = _NETWORKSTATS -DESCRIPTOR.message_types_by_name['BatteryStateRequest'] = _BATTERYSTATEREQUEST -DESCRIPTOR.message_types_by_name['BatteryStateResponse'] = _BATTERYSTATERESPONSE -DESCRIPTOR.message_types_by_name['CubeBattery'] = _CUBEBATTERY -DESCRIPTOR.message_types_by_name['VersionStateRequest'] = _VERSIONSTATEREQUEST -DESCRIPTOR.message_types_by_name['VersionStateResponse'] = _VERSIONSTATERESPONSE -DESCRIPTOR.message_types_by_name['NetworkStateRequest'] = _NETWORKSTATEREQUEST -DESCRIPTOR.message_types_by_name['NetworkStateResponse'] = _NETWORKSTATERESPONSE -DESCRIPTOR.message_types_by_name['SayTextRequest'] = _SAYTEXTREQUEST -DESCRIPTOR.message_types_by_name['SayTextResponse'] = _SAYTEXTRESPONSE -DESCRIPTOR.message_types_by_name['StimulationInfo'] = _STIMULATIONINFO -DESCRIPTOR.message_types_by_name['AudioSendModeRequest'] = _AUDIOSENDMODEREQUEST -DESCRIPTOR.message_types_by_name['AudioSendModeChanged'] = _AUDIOSENDMODECHANGED -DESCRIPTOR.message_types_by_name['AudioChunk'] = _AUDIOCHUNK -DESCRIPTOR.message_types_by_name['AudioFeedRequest'] = _AUDIOFEEDREQUEST -DESCRIPTOR.message_types_by_name['AudioFeedResponse'] = _AUDIOFEEDRESPONSE -DESCRIPTOR.message_types_by_name['EnableMarkerDetectionRequest'] = _ENABLEMARKERDETECTIONREQUEST -DESCRIPTOR.message_types_by_name['EnableMarkerDetectionResponse'] = _ENABLEMARKERDETECTIONRESPONSE -DESCRIPTOR.message_types_by_name['EnableFaceDetectionRequest'] = _ENABLEFACEDETECTIONREQUEST -DESCRIPTOR.message_types_by_name['EnableFaceDetectionResponse'] = _ENABLEFACEDETECTIONRESPONSE -DESCRIPTOR.message_types_by_name['EnableMotionDetectionRequest'] = _ENABLEMOTIONDETECTIONREQUEST -DESCRIPTOR.message_types_by_name['EnableMotionDetectionResponse'] = _ENABLEMOTIONDETECTIONRESPONSE -DESCRIPTOR.message_types_by_name['EnableMirrorModeRequest'] = _ENABLEMIRRORMODEREQUEST -DESCRIPTOR.message_types_by_name['EnableMirrorModeResponse'] = _ENABLEMIRRORMODERESPONSE -DESCRIPTOR.message_types_by_name['MirrorModeDisabled'] = _MIRRORMODEDISABLED -DESCRIPTOR.message_types_by_name['EnableImageStreamingRequest'] = _ENABLEIMAGESTREAMINGREQUEST -DESCRIPTOR.message_types_by_name['EnableImageStreamingResponse'] = _ENABLEIMAGESTREAMINGRESPONSE -DESCRIPTOR.message_types_by_name['VisionModesAutoDisabled'] = _VISIONMODESAUTODISABLED -DESCRIPTOR.message_types_by_name['ImageChunk'] = _IMAGECHUNK -DESCRIPTOR.message_types_by_name['CameraFeedRequest'] = _CAMERAFEEDREQUEST -DESCRIPTOR.message_types_by_name['CameraFeedResponse'] = _CAMERAFEEDRESPONSE -DESCRIPTOR.message_types_by_name['SetEyeColorRequest'] = _SETEYECOLORREQUEST -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 -DESCRIPTOR.enum_types_by_name['ActionTagConstants'] = _ACTIONTAGCONSTANTS -DESCRIPTOR.enum_types_by_name['AlignmentType'] = _ALIGNMENTTYPE -DESCRIPTOR.enum_types_by_name['BatteryLevel'] = _BATTERYLEVEL -DESCRIPTOR.enum_types_by_name['AudioConstants'] = _AUDIOCONSTANTS -DESCRIPTOR.enum_types_by_name['AudioProcessingMode'] = _AUDIOPROCESSINGMODE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -KeepAlivePing = _reflection.GeneratedProtocolMessageType('KeepAlivePing', (_message.Message,), dict( - DESCRIPTOR = _KEEPALIVEPING, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.KeepAlivePing) - )) -_sym_db.RegisterMessage(KeepAlivePing) - -Animation = _reflection.GeneratedProtocolMessageType('Animation', (_message.Message,), dict( - DESCRIPTOR = _ANIMATION, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.Animation) - )) -_sym_db.RegisterMessage(Animation) - -DriveWheelsRequest = _reflection.GeneratedProtocolMessageType('DriveWheelsRequest', (_message.Message,), dict( - DESCRIPTOR = _DRIVEWHEELSREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DriveWheelsRequest) - )) -_sym_db.RegisterMessage(DriveWheelsRequest) - -DriveWheelsResponse = _reflection.GeneratedProtocolMessageType('DriveWheelsResponse', (_message.Message,), dict( - DESCRIPTOR = _DRIVEWHEELSRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DriveWheelsResponse) - )) -_sym_db.RegisterMessage(DriveWheelsResponse) - -MoveHeadRequest = _reflection.GeneratedProtocolMessageType('MoveHeadRequest', (_message.Message,), dict( - DESCRIPTOR = _MOVEHEADREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.MoveHeadRequest) - )) -_sym_db.RegisterMessage(MoveHeadRequest) - -MoveHeadResponse = _reflection.GeneratedProtocolMessageType('MoveHeadResponse', (_message.Message,), dict( - DESCRIPTOR = _MOVEHEADRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.MoveHeadResponse) - )) -_sym_db.RegisterMessage(MoveHeadResponse) - -MoveLiftRequest = _reflection.GeneratedProtocolMessageType('MoveLiftRequest', (_message.Message,), dict( - DESCRIPTOR = _MOVELIFTREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.MoveLiftRequest) - )) -_sym_db.RegisterMessage(MoveLiftRequest) - -MoveLiftResponse = _reflection.GeneratedProtocolMessageType('MoveLiftResponse', (_message.Message,), dict( - DESCRIPTOR = _MOVELIFTRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.MoveLiftResponse) - )) -_sym_db.RegisterMessage(MoveLiftResponse) - -PlayAnimationRequest = _reflection.GeneratedProtocolMessageType('PlayAnimationRequest', (_message.Message,), dict( - DESCRIPTOR = _PLAYANIMATIONREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PlayAnimationRequest) - )) -_sym_db.RegisterMessage(PlayAnimationRequest) - -PlayAnimationResponse = _reflection.GeneratedProtocolMessageType('PlayAnimationResponse', (_message.Message,), dict( - DESCRIPTOR = _PLAYANIMATIONRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PlayAnimationResponse) - )) -_sym_db.RegisterMessage(PlayAnimationResponse) - -ListAnimationsRequest = _reflection.GeneratedProtocolMessageType('ListAnimationsRequest', (_message.Message,), dict( - DESCRIPTOR = _LISTANIMATIONSREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ListAnimationsRequest) - )) -_sym_db.RegisterMessage(ListAnimationsRequest) - -ListAnimationsResponse = _reflection.GeneratedProtocolMessageType('ListAnimationsResponse', (_message.Message,), dict( - DESCRIPTOR = _LISTANIMATIONSRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ListAnimationsResponse) - )) -_sym_db.RegisterMessage(ListAnimationsResponse) - -DisplayFaceImageRGBRequest = _reflection.GeneratedProtocolMessageType('DisplayFaceImageRGBRequest', (_message.Message,), dict( - DESCRIPTOR = _DISPLAYFACEIMAGERGBREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DisplayFaceImageRGBRequest) - )) -_sym_db.RegisterMessage(DisplayFaceImageRGBRequest) - -DisplayFaceImageRGBResponse = _reflection.GeneratedProtocolMessageType('DisplayFaceImageRGBResponse', (_message.Message,), dict( - DESCRIPTOR = _DISPLAYFACEIMAGERGBRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DisplayFaceImageRGBResponse) - )) -_sym_db.RegisterMessage(DisplayFaceImageRGBResponse) - -MeetVictorFaceScanStarted = _reflection.GeneratedProtocolMessageType('MeetVictorFaceScanStarted', (_message.Message,), dict( - DESCRIPTOR = _MEETVICTORFACESCANSTARTED, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.MeetVictorFaceScanStarted) - )) -_sym_db.RegisterMessage(MeetVictorFaceScanStarted) - -MeetVictorFaceScanComplete = _reflection.GeneratedProtocolMessageType('MeetVictorFaceScanComplete', (_message.Message,), dict( - DESCRIPTOR = _MEETVICTORFACESCANCOMPLETE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.MeetVictorFaceScanComplete) - )) -_sym_db.RegisterMessage(MeetVictorFaceScanComplete) - -Status = _reflection.GeneratedProtocolMessageType('Status', (_message.Message,), dict( - DESCRIPTOR = _STATUS, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.Status) - )) -_sym_db.RegisterMessage(Status) - -WakeWord = _reflection.GeneratedProtocolMessageType('WakeWord', (_message.Message,), dict( - DESCRIPTOR = _WAKEWORD, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.WakeWord) - )) -_sym_db.RegisterMessage(WakeWord) - -TimeStampedStatus = _reflection.GeneratedProtocolMessageType('TimeStampedStatus', (_message.Message,), dict( - DESCRIPTOR = _TIMESTAMPEDSTATUS, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.TimeStampedStatus) - )) -_sym_db.RegisterMessage(TimeStampedStatus) - -PoseStruct = _reflection.GeneratedProtocolMessageType('PoseStruct', (_message.Message,), dict( - DESCRIPTOR = _POSESTRUCT, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PoseStruct) - )) -_sym_db.RegisterMessage(PoseStruct) - -AccelData = _reflection.GeneratedProtocolMessageType('AccelData', (_message.Message,), dict( - DESCRIPTOR = _ACCELDATA, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.AccelData) - )) -_sym_db.RegisterMessage(AccelData) - -GyroData = _reflection.GeneratedProtocolMessageType('GyroData', (_message.Message,), dict( - DESCRIPTOR = _GYRODATA, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.GyroData) - )) -_sym_db.RegisterMessage(GyroData) - -ProxData = _reflection.GeneratedProtocolMessageType('ProxData', (_message.Message,), dict( - DESCRIPTOR = _PROXDATA, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ProxData) - )) -_sym_db.RegisterMessage(ProxData) - -TouchData = _reflection.GeneratedProtocolMessageType('TouchData', (_message.Message,), dict( - DESCRIPTOR = _TOUCHDATA, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.TouchData) - )) -_sym_db.RegisterMessage(TouchData) - -RobotState = _reflection.GeneratedProtocolMessageType('RobotState', (_message.Message,), dict( - DESCRIPTOR = _ROBOTSTATE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.RobotState) - )) -_sym_db.RegisterMessage(RobotState) - -CladPoint = _reflection.GeneratedProtocolMessageType('CladPoint', (_message.Message,), dict( - DESCRIPTOR = _CLADPOINT, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CladPoint) - )) -_sym_db.RegisterMessage(CladPoint) - -CladRect = _reflection.GeneratedProtocolMessageType('CladRect', (_message.Message,), dict( - DESCRIPTOR = _CLADRECT, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CladRect) - )) -_sym_db.RegisterMessage(CladRect) - -RobotObservedFace = _reflection.GeneratedProtocolMessageType('RobotObservedFace', (_message.Message,), dict( - DESCRIPTOR = _ROBOTOBSERVEDFACE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.RobotObservedFace) - )) -_sym_db.RegisterMessage(RobotObservedFace) - -RobotChangedObservedFaceID = _reflection.GeneratedProtocolMessageType('RobotChangedObservedFaceID', (_message.Message,), dict( - DESCRIPTOR = _ROBOTCHANGEDOBSERVEDFACEID, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.RobotChangedObservedFaceID) - )) -_sym_db.RegisterMessage(RobotChangedObservedFaceID) - -AppIntentRequest = _reflection.GeneratedProtocolMessageType('AppIntentRequest', (_message.Message,), dict( - DESCRIPTOR = _APPINTENTREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.AppIntentRequest) - )) -_sym_db.RegisterMessage(AppIntentRequest) - -AppDisconnected = _reflection.GeneratedProtocolMessageType('AppDisconnected', (_message.Message,), dict( - DESCRIPTOR = _APPDISCONNECTED, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.AppDisconnected) - )) -_sym_db.RegisterMessage(AppDisconnected) - -AppIntentResponse = _reflection.GeneratedProtocolMessageType('AppIntentResponse', (_message.Message,), dict( - DESCRIPTOR = _APPINTENTRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.AppIntentResponse) - )) -_sym_db.RegisterMessage(AppIntentResponse) - -FaceEnrollmentCompleted = _reflection.GeneratedProtocolMessageType('FaceEnrollmentCompleted', (_message.Message,), dict( - DESCRIPTOR = _FACEENROLLMENTCOMPLETED, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.FaceEnrollmentCompleted) - )) -_sym_db.RegisterMessage(FaceEnrollmentCompleted) - -CancelFaceEnrollmentRequest = _reflection.GeneratedProtocolMessageType('CancelFaceEnrollmentRequest', (_message.Message,), dict( - DESCRIPTOR = _CANCELFACEENROLLMENTREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CancelFaceEnrollmentRequest) - )) -_sym_db.RegisterMessage(CancelFaceEnrollmentRequest) - -CancelFaceEnrollmentResponse = _reflection.GeneratedProtocolMessageType('CancelFaceEnrollmentResponse', (_message.Message,), dict( - DESCRIPTOR = _CANCELFACEENROLLMENTRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CancelFaceEnrollmentResponse) - )) -_sym_db.RegisterMessage(CancelFaceEnrollmentResponse) - -RequestEnrolledNamesRequest = _reflection.GeneratedProtocolMessageType('RequestEnrolledNamesRequest', (_message.Message,), dict( - DESCRIPTOR = _REQUESTENROLLEDNAMESREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.RequestEnrolledNamesRequest) - )) -_sym_db.RegisterMessage(RequestEnrolledNamesRequest) - -LoadedKnownFace = _reflection.GeneratedProtocolMessageType('LoadedKnownFace', (_message.Message,), dict( - DESCRIPTOR = _LOADEDKNOWNFACE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.LoadedKnownFace) - )) -_sym_db.RegisterMessage(LoadedKnownFace) - -RobotRenamedEnrolledFace = _reflection.GeneratedProtocolMessageType('RobotRenamedEnrolledFace', (_message.Message,), dict( - DESCRIPTOR = _ROBOTRENAMEDENROLLEDFACE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.RobotRenamedEnrolledFace) - )) -_sym_db.RegisterMessage(RobotRenamedEnrolledFace) - -RequestEnrolledNamesResponse = _reflection.GeneratedProtocolMessageType('RequestEnrolledNamesResponse', (_message.Message,), dict( - DESCRIPTOR = _REQUESTENROLLEDNAMESRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.RequestEnrolledNamesResponse) - )) -_sym_db.RegisterMessage(RequestEnrolledNamesResponse) - -UpdateEnrolledFaceByIDRequest = _reflection.GeneratedProtocolMessageType('UpdateEnrolledFaceByIDRequest', (_message.Message,), dict( - DESCRIPTOR = _UPDATEENROLLEDFACEBYIDREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.UpdateEnrolledFaceByIDRequest) - )) -_sym_db.RegisterMessage(UpdateEnrolledFaceByIDRequest) - -UpdateEnrolledFaceByIDResponse = _reflection.GeneratedProtocolMessageType('UpdateEnrolledFaceByIDResponse', (_message.Message,), dict( - DESCRIPTOR = _UPDATEENROLLEDFACEBYIDRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.UpdateEnrolledFaceByIDResponse) - )) -_sym_db.RegisterMessage(UpdateEnrolledFaceByIDResponse) - -EraseEnrolledFaceByIDRequest = _reflection.GeneratedProtocolMessageType('EraseEnrolledFaceByIDRequest', (_message.Message,), dict( - DESCRIPTOR = _ERASEENROLLEDFACEBYIDREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EraseEnrolledFaceByIDRequest) - )) -_sym_db.RegisterMessage(EraseEnrolledFaceByIDRequest) - -EraseEnrolledFaceByIDResponse = _reflection.GeneratedProtocolMessageType('EraseEnrolledFaceByIDResponse', (_message.Message,), dict( - DESCRIPTOR = _ERASEENROLLEDFACEBYIDRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EraseEnrolledFaceByIDResponse) - )) -_sym_db.RegisterMessage(EraseEnrolledFaceByIDResponse) - -EraseAllEnrolledFacesRequest = _reflection.GeneratedProtocolMessageType('EraseAllEnrolledFacesRequest', (_message.Message,), dict( - DESCRIPTOR = _ERASEALLENROLLEDFACESREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EraseAllEnrolledFacesRequest) - )) -_sym_db.RegisterMessage(EraseAllEnrolledFacesRequest) - -EraseAllEnrolledFacesResponse = _reflection.GeneratedProtocolMessageType('EraseAllEnrolledFacesResponse', (_message.Message,), dict( - DESCRIPTOR = _ERASEALLENROLLEDFACESRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EraseAllEnrolledFacesResponse) - )) -_sym_db.RegisterMessage(EraseAllEnrolledFacesResponse) - -SetFaceToEnrollRequest = _reflection.GeneratedProtocolMessageType('SetFaceToEnrollRequest', (_message.Message,), dict( - DESCRIPTOR = _SETFACETOENROLLREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetFaceToEnrollRequest) - )) -_sym_db.RegisterMessage(SetFaceToEnrollRequest) - -SetFaceToEnrollResponse = _reflection.GeneratedProtocolMessageType('SetFaceToEnrollResponse', (_message.Message,), dict( - DESCRIPTOR = _SETFACETOENROLLRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetFaceToEnrollResponse) - )) -_sym_db.RegisterMessage(SetFaceToEnrollResponse) - -DriveOffChargerRequest = _reflection.GeneratedProtocolMessageType('DriveOffChargerRequest', (_message.Message,), dict( - DESCRIPTOR = _DRIVEOFFCHARGERREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DriveOffChargerRequest) - )) -_sym_db.RegisterMessage(DriveOffChargerRequest) - -DriveOffChargerResponse = _reflection.GeneratedProtocolMessageType('DriveOffChargerResponse', (_message.Message,), dict( - DESCRIPTOR = _DRIVEOFFCHARGERRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DriveOffChargerResponse) - )) -_sym_db.RegisterMessage(DriveOffChargerResponse) - -DriveOnChargerRequest = _reflection.GeneratedProtocolMessageType('DriveOnChargerRequest', (_message.Message,), dict( - DESCRIPTOR = _DRIVEONCHARGERREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DriveOnChargerRequest) - )) -_sym_db.RegisterMessage(DriveOnChargerRequest) - -DriveOnChargerResponse = _reflection.GeneratedProtocolMessageType('DriveOnChargerResponse', (_message.Message,), dict( - DESCRIPTOR = _DRIVEONCHARGERRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DriveOnChargerResponse) - )) -_sym_db.RegisterMessage(DriveOnChargerResponse) - -WakeWordBegin = _reflection.GeneratedProtocolMessageType('WakeWordBegin', (_message.Message,), dict( - DESCRIPTOR = _WAKEWORDBEGIN, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.WakeWordBegin) - )) -_sym_db.RegisterMessage(WakeWordBegin) - -WakeWordEnd = _reflection.GeneratedProtocolMessageType('WakeWordEnd', (_message.Message,), dict( - DESCRIPTOR = _WAKEWORDEND, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.WakeWordEnd) - )) -_sym_db.RegisterMessage(WakeWordEnd) - -PhotoInfo = _reflection.GeneratedProtocolMessageType('PhotoInfo', (_message.Message,), dict( - DESCRIPTOR = _PHOTOINFO, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PhotoInfo) - )) -_sym_db.RegisterMessage(PhotoInfo) - -PhotosInfoRequest = _reflection.GeneratedProtocolMessageType('PhotosInfoRequest', (_message.Message,), dict( - DESCRIPTOR = _PHOTOSINFOREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PhotosInfoRequest) - )) -_sym_db.RegisterMessage(PhotosInfoRequest) - -PhotosInfoResponse = _reflection.GeneratedProtocolMessageType('PhotosInfoResponse', (_message.Message,), dict( - DESCRIPTOR = _PHOTOSINFORESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PhotosInfoResponse) - )) -_sym_db.RegisterMessage(PhotosInfoResponse) - -PhotoRequest = _reflection.GeneratedProtocolMessageType('PhotoRequest', (_message.Message,), dict( - DESCRIPTOR = _PHOTOREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PhotoRequest) - )) -_sym_db.RegisterMessage(PhotoRequest) - -PhotoPathMessage = _reflection.GeneratedProtocolMessageType('PhotoPathMessage', (_message.Message,), dict( - DESCRIPTOR = _PHOTOPATHMESSAGE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PhotoPathMessage) - )) -_sym_db.RegisterMessage(PhotoPathMessage) - -PhotoResponse = _reflection.GeneratedProtocolMessageType('PhotoResponse', (_message.Message,), dict( - DESCRIPTOR = _PHOTORESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PhotoResponse) - )) -_sym_db.RegisterMessage(PhotoResponse) - -ThumbnailRequest = _reflection.GeneratedProtocolMessageType('ThumbnailRequest', (_message.Message,), dict( - DESCRIPTOR = _THUMBNAILREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ThumbnailRequest) - )) -_sym_db.RegisterMessage(ThumbnailRequest) - -ThumbnailPathMessage = _reflection.GeneratedProtocolMessageType('ThumbnailPathMessage', (_message.Message,), dict( - DESCRIPTOR = _THUMBNAILPATHMESSAGE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ThumbnailPathMessage) - )) -_sym_db.RegisterMessage(ThumbnailPathMessage) - -ThumbnailResponse = _reflection.GeneratedProtocolMessageType('ThumbnailResponse', (_message.Message,), dict( - DESCRIPTOR = _THUMBNAILRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ThumbnailResponse) - )) -_sym_db.RegisterMessage(ThumbnailResponse) - -DeletePhotoRequest = _reflection.GeneratedProtocolMessageType('DeletePhotoRequest', (_message.Message,), dict( - DESCRIPTOR = _DELETEPHOTOREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DeletePhotoRequest) - )) -_sym_db.RegisterMessage(DeletePhotoRequest) - -DeletePhotoResponse = _reflection.GeneratedProtocolMessageType('DeletePhotoResponse', (_message.Message,), dict( - DESCRIPTOR = _DELETEPHOTORESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DeletePhotoResponse) - )) -_sym_db.RegisterMessage(DeletePhotoResponse) - -PhotoTaken = _reflection.GeneratedProtocolMessageType('PhotoTaken', (_message.Message,), dict( - DESCRIPTOR = _PHOTOTAKEN, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PhotoTaken) - )) -_sym_db.RegisterMessage(PhotoTaken) - -PathMotionProfile = _reflection.GeneratedProtocolMessageType('PathMotionProfile', (_message.Message,), dict( - DESCRIPTOR = _PATHMOTIONPROFILE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.PathMotionProfile) - )) -_sym_db.RegisterMessage(PathMotionProfile) - -ActionResult = _reflection.GeneratedProtocolMessageType('ActionResult', (_message.Message,), dict( - DESCRIPTOR = _ACTIONRESULT, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ActionResult) - )) -_sym_db.RegisterMessage(ActionResult) - -GoToPoseRequest = _reflection.GeneratedProtocolMessageType('GoToPoseRequest', (_message.Message,), dict( - DESCRIPTOR = _GOTOPOSEREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.GoToPoseRequest) - )) -_sym_db.RegisterMessage(GoToPoseRequest) - -GoToPoseResponse = _reflection.GeneratedProtocolMessageType('GoToPoseResponse', (_message.Message,), dict( - DESCRIPTOR = _GOTOPOSERESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.GoToPoseResponse) - )) -_sym_db.RegisterMessage(GoToPoseResponse) - -DockWithCubeRequest = _reflection.GeneratedProtocolMessageType('DockWithCubeRequest', (_message.Message,), dict( - DESCRIPTOR = _DOCKWITHCUBEREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DockWithCubeRequest) - )) -_sym_db.RegisterMessage(DockWithCubeRequest) - -DockWithCubeResponse = _reflection.GeneratedProtocolMessageType('DockWithCubeResponse', (_message.Message,), dict( - DESCRIPTOR = _DOCKWITHCUBERESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DockWithCubeResponse) - )) -_sym_db.RegisterMessage(DockWithCubeResponse) - -DriveStraightRequest = _reflection.GeneratedProtocolMessageType('DriveStraightRequest', (_message.Message,), dict( - DESCRIPTOR = _DRIVESTRAIGHTREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DriveStraightRequest) - )) -_sym_db.RegisterMessage(DriveStraightRequest) - -DriveStraightResponse = _reflection.GeneratedProtocolMessageType('DriveStraightResponse', (_message.Message,), dict( - DESCRIPTOR = _DRIVESTRAIGHTRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.DriveStraightResponse) - )) -_sym_db.RegisterMessage(DriveStraightResponse) - -TurnInPlaceRequest = _reflection.GeneratedProtocolMessageType('TurnInPlaceRequest', (_message.Message,), dict( - DESCRIPTOR = _TURNINPLACEREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.TurnInPlaceRequest) - )) -_sym_db.RegisterMessage(TurnInPlaceRequest) - -TurnInPlaceResponse = _reflection.GeneratedProtocolMessageType('TurnInPlaceResponse', (_message.Message,), dict( - DESCRIPTOR = _TURNINPLACERESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.TurnInPlaceResponse) - )) -_sym_db.RegisterMessage(TurnInPlaceResponse) - -SetHeadAngleRequest = _reflection.GeneratedProtocolMessageType('SetHeadAngleRequest', (_message.Message,), dict( - DESCRIPTOR = _SETHEADANGLEREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetHeadAngleRequest) - )) -_sym_db.RegisterMessage(SetHeadAngleRequest) - -SetHeadAngleResponse = _reflection.GeneratedProtocolMessageType('SetHeadAngleResponse', (_message.Message,), dict( - DESCRIPTOR = _SETHEADANGLERESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetHeadAngleResponse) - )) -_sym_db.RegisterMessage(SetHeadAngleResponse) - -SetLiftHeightRequest = _reflection.GeneratedProtocolMessageType('SetLiftHeightRequest', (_message.Message,), dict( - DESCRIPTOR = _SETLIFTHEIGHTREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetLiftHeightRequest) - )) -_sym_db.RegisterMessage(SetLiftHeightRequest) - -SetLiftHeightResponse = _reflection.GeneratedProtocolMessageType('SetLiftHeightResponse', (_message.Message,), dict( - DESCRIPTOR = _SETLIFTHEIGHTRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetLiftHeightResponse) - )) -_sym_db.RegisterMessage(SetLiftHeightResponse) - -NetworkStats = _reflection.GeneratedProtocolMessageType('NetworkStats', (_message.Message,), dict( - DESCRIPTOR = _NETWORKSTATS, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.NetworkStats) - )) -_sym_db.RegisterMessage(NetworkStats) - -BatteryStateRequest = _reflection.GeneratedProtocolMessageType('BatteryStateRequest', (_message.Message,), dict( - DESCRIPTOR = _BATTERYSTATEREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.BatteryStateRequest) - )) -_sym_db.RegisterMessage(BatteryStateRequest) - -BatteryStateResponse = _reflection.GeneratedProtocolMessageType('BatteryStateResponse', (_message.Message,), dict( - DESCRIPTOR = _BATTERYSTATERESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.BatteryStateResponse) - )) -_sym_db.RegisterMessage(BatteryStateResponse) - -CubeBattery = _reflection.GeneratedProtocolMessageType('CubeBattery', (_message.Message,), dict( - DESCRIPTOR = _CUBEBATTERY, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CubeBattery) - )) -_sym_db.RegisterMessage(CubeBattery) - -VersionStateRequest = _reflection.GeneratedProtocolMessageType('VersionStateRequest', (_message.Message,), dict( - DESCRIPTOR = _VERSIONSTATEREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.VersionStateRequest) - )) -_sym_db.RegisterMessage(VersionStateRequest) - -VersionStateResponse = _reflection.GeneratedProtocolMessageType('VersionStateResponse', (_message.Message,), dict( - DESCRIPTOR = _VERSIONSTATERESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.VersionStateResponse) - )) -_sym_db.RegisterMessage(VersionStateResponse) - -NetworkStateRequest = _reflection.GeneratedProtocolMessageType('NetworkStateRequest', (_message.Message,), dict( - DESCRIPTOR = _NETWORKSTATEREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.NetworkStateRequest) - )) -_sym_db.RegisterMessage(NetworkStateRequest) - -NetworkStateResponse = _reflection.GeneratedProtocolMessageType('NetworkStateResponse', (_message.Message,), dict( - DESCRIPTOR = _NETWORKSTATERESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.NetworkStateResponse) - )) -_sym_db.RegisterMessage(NetworkStateResponse) - -SayTextRequest = _reflection.GeneratedProtocolMessageType('SayTextRequest', (_message.Message,), dict( - DESCRIPTOR = _SAYTEXTREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SayTextRequest) - )) -_sym_db.RegisterMessage(SayTextRequest) - -SayTextResponse = _reflection.GeneratedProtocolMessageType('SayTextResponse', (_message.Message,), dict( - DESCRIPTOR = _SAYTEXTRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SayTextResponse) - )) -_sym_db.RegisterMessage(SayTextResponse) - -StimulationInfo = _reflection.GeneratedProtocolMessageType('StimulationInfo', (_message.Message,), dict( - DESCRIPTOR = _STIMULATIONINFO, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.StimulationInfo) - )) -_sym_db.RegisterMessage(StimulationInfo) - -AudioSendModeRequest = _reflection.GeneratedProtocolMessageType('AudioSendModeRequest', (_message.Message,), dict( - DESCRIPTOR = _AUDIOSENDMODEREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.AudioSendModeRequest) - )) -_sym_db.RegisterMessage(AudioSendModeRequest) - -AudioSendModeChanged = _reflection.GeneratedProtocolMessageType('AudioSendModeChanged', (_message.Message,), dict( - DESCRIPTOR = _AUDIOSENDMODECHANGED, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.AudioSendModeChanged) - )) -_sym_db.RegisterMessage(AudioSendModeChanged) - -AudioChunk = _reflection.GeneratedProtocolMessageType('AudioChunk', (_message.Message,), dict( - DESCRIPTOR = _AUDIOCHUNK, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.AudioChunk) - )) -_sym_db.RegisterMessage(AudioChunk) - -AudioFeedRequest = _reflection.GeneratedProtocolMessageType('AudioFeedRequest', (_message.Message,), dict( - DESCRIPTOR = _AUDIOFEEDREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.AudioFeedRequest) - )) -_sym_db.RegisterMessage(AudioFeedRequest) - -AudioFeedResponse = _reflection.GeneratedProtocolMessageType('AudioFeedResponse', (_message.Message,), dict( - DESCRIPTOR = _AUDIOFEEDRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.AudioFeedResponse) - )) -_sym_db.RegisterMessage(AudioFeedResponse) - -EnableMarkerDetectionRequest = _reflection.GeneratedProtocolMessageType('EnableMarkerDetectionRequest', (_message.Message,), dict( - DESCRIPTOR = _ENABLEMARKERDETECTIONREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableMarkerDetectionRequest) - )) -_sym_db.RegisterMessage(EnableMarkerDetectionRequest) - -EnableMarkerDetectionResponse = _reflection.GeneratedProtocolMessageType('EnableMarkerDetectionResponse', (_message.Message,), dict( - DESCRIPTOR = _ENABLEMARKERDETECTIONRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableMarkerDetectionResponse) - )) -_sym_db.RegisterMessage(EnableMarkerDetectionResponse) - -EnableFaceDetectionRequest = _reflection.GeneratedProtocolMessageType('EnableFaceDetectionRequest', (_message.Message,), dict( - DESCRIPTOR = _ENABLEFACEDETECTIONREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableFaceDetectionRequest) - )) -_sym_db.RegisterMessage(EnableFaceDetectionRequest) - -EnableFaceDetectionResponse = _reflection.GeneratedProtocolMessageType('EnableFaceDetectionResponse', (_message.Message,), dict( - DESCRIPTOR = _ENABLEFACEDETECTIONRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableFaceDetectionResponse) - )) -_sym_db.RegisterMessage(EnableFaceDetectionResponse) - -EnableMotionDetectionRequest = _reflection.GeneratedProtocolMessageType('EnableMotionDetectionRequest', (_message.Message,), dict( - DESCRIPTOR = _ENABLEMOTIONDETECTIONREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableMotionDetectionRequest) - )) -_sym_db.RegisterMessage(EnableMotionDetectionRequest) - -EnableMotionDetectionResponse = _reflection.GeneratedProtocolMessageType('EnableMotionDetectionResponse', (_message.Message,), dict( - DESCRIPTOR = _ENABLEMOTIONDETECTIONRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableMotionDetectionResponse) - )) -_sym_db.RegisterMessage(EnableMotionDetectionResponse) - -EnableMirrorModeRequest = _reflection.GeneratedProtocolMessageType('EnableMirrorModeRequest', (_message.Message,), dict( - DESCRIPTOR = _ENABLEMIRRORMODEREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableMirrorModeRequest) - )) -_sym_db.RegisterMessage(EnableMirrorModeRequest) - -EnableMirrorModeResponse = _reflection.GeneratedProtocolMessageType('EnableMirrorModeResponse', (_message.Message,), dict( - DESCRIPTOR = _ENABLEMIRRORMODERESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableMirrorModeResponse) - )) -_sym_db.RegisterMessage(EnableMirrorModeResponse) - -MirrorModeDisabled = _reflection.GeneratedProtocolMessageType('MirrorModeDisabled', (_message.Message,), dict( - DESCRIPTOR = _MIRRORMODEDISABLED, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.MirrorModeDisabled) - )) -_sym_db.RegisterMessage(MirrorModeDisabled) - -EnableImageStreamingRequest = _reflection.GeneratedProtocolMessageType('EnableImageStreamingRequest', (_message.Message,), dict( - DESCRIPTOR = _ENABLEIMAGESTREAMINGREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableImageStreamingRequest) - )) -_sym_db.RegisterMessage(EnableImageStreamingRequest) - -EnableImageStreamingResponse = _reflection.GeneratedProtocolMessageType('EnableImageStreamingResponse', (_message.Message,), dict( - DESCRIPTOR = _ENABLEIMAGESTREAMINGRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EnableImageStreamingResponse) - )) -_sym_db.RegisterMessage(EnableImageStreamingResponse) - -VisionModesAutoDisabled = _reflection.GeneratedProtocolMessageType('VisionModesAutoDisabled', (_message.Message,), dict( - DESCRIPTOR = _VISIONMODESAUTODISABLED, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.VisionModesAutoDisabled) - )) -_sym_db.RegisterMessage(VisionModesAutoDisabled) - -ImageChunk = _reflection.GeneratedProtocolMessageType('ImageChunk', (_message.Message,), dict( - DESCRIPTOR = _IMAGECHUNK, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ImageChunk) - )) -_sym_db.RegisterMessage(ImageChunk) - -CameraFeedRequest = _reflection.GeneratedProtocolMessageType('CameraFeedRequest', (_message.Message,), dict( - DESCRIPTOR = _CAMERAFEEDREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CameraFeedRequest) - )) -_sym_db.RegisterMessage(CameraFeedRequest) - -CameraFeedResponse = _reflection.GeneratedProtocolMessageType('CameraFeedResponse', (_message.Message,), dict( - DESCRIPTOR = _CAMERAFEEDRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.CameraFeedResponse) - )) -_sym_db.RegisterMessage(CameraFeedResponse) - -SetEyeColorRequest = _reflection.GeneratedProtocolMessageType('SetEyeColorRequest', (_message.Message,), dict( - DESCRIPTOR = _SETEYECOLORREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetEyeColorRequest) - )) -_sym_db.RegisterMessage(SetEyeColorRequest) - -SetEyeColorResponse = _reflection.GeneratedProtocolMessageType('SetEyeColorResponse', (_message.Message,), dict( - DESCRIPTOR = _SETEYECOLORRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SetEyeColorResponse) - )) -_sym_db.RegisterMessage(SetEyeColorResponse) - -SDKInitializationRequest = _reflection.GeneratedProtocolMessageType('SDKInitializationRequest', (_message.Message,), dict( - DESCRIPTOR = _SDKINITIALIZATIONREQUEST, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SDKInitializationRequest) - )) -_sym_db.RegisterMessage(SDKInitializationRequest) - -SDKInitializationResponse = _reflection.GeneratedProtocolMessageType('SDKInitializationResponse', (_message.Message,), dict( - DESCRIPTOR = _SDKINITIALIZATIONRESPONSE, - __module__ = 'anki_vector.messaging.messages_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.SDKInitializationResponse) - )) -_sym_db.RegisterMessage(SDKInitializationResponse) - - -_FACIALEXPRESSION.has_options = True -_FACIALEXPRESSION._options = _descriptor._ParseOptions(descriptor_pb2.EnumOptions(), _b('\020\001')) -_AUDIOFEEDRESPONSE.has_options = True -_AUDIOFEEDRESPONSE._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\200\246\035\001')) -_CAMERAFEEDRESPONSE.has_options = True -_CAMERAFEEDRESPONSE._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\200\246\035\001')) -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/messages_pb2_grpc.py b/anki_vector/messaging/messages_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/messages_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/nav_map.proto b/anki_vector/messaging/nav_map.proto deleted file mode 100644 index 21c97a6..0000000 --- a/anki_vector/messaging/nav_map.proto +++ /dev/null @@ -1,75 +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. - -// External interface for robot <-> app and robot <-> sdk communication -// about the robot's navigational memory map. - -syntax = "proto3"; - -package Anki.Vector.external_interface; - -// Every tile in the nav map will be tagged with a content key referring to -// the different environmental elements that Vector can identify. -enum NavNodeContentType -{ - NAV_NODE_UNKNOWN = 0; - NAV_NODE_CLEAR_OF_OBSTACLE = 1; - NAV_NODE_CLEAR_OF_CLIFF = 2; - NAV_NODE_OBSTACLE_CUBE = 3; - NAV_NODE_OBSTACLE_PROXIMITY = 4; - NAV_NODE_OBSTACLE_PROXIMITY_EXPLORED = 5; - NAV_NODE_OBSTACLE_UNRECOGNIZED = 6; - NAV_NODE_CLIFF = 7; - NAV_NODE_INTERESTING_EDGE = 8; - NAV_NODE_NON_INTERESTING_EDGE = 9; -} - -// An individual sample of vector's nav map. This quad's size will vary and -// depends on the resolution the map requires to effectively identify -// boundaries in the environment. -message NavMapQuadInfo -{ - NavNodeContentType content = 1; - uint32 depth = 2; - uint32 color_rgba = 3; -} - -// General information about the nav map as a whole. -message NavMapInfo -{ - int32 root_depth = 1; - float root_size_mm = 2; - float root_center_x = 3; - float root_center_y = 4; - float root_center_z = 5; -} - -// Requests nav map data from the robot at a specified maximum update frequency. -// Responses in the nav map stream may be sent less frequently if the robot does -// not consider there to be relevant new information. -message NavMapFeedRequest -{ - float frequency = 1; -} - -// A full nav map sent from the robot. It contains an origin_id that -// which can be compared against the robot's current origin_id, general -// info about the map, and a collection of quads representing the map's -// content. -message NavMapFeedResponse -{ - uint32 origin_id = 1; - NavMapInfo map_info = 2; - repeated NavMapQuadInfo quad_infos = 3; -} diff --git a/anki_vector/messaging/nav_map_pb2.py b/anki_vector/messaging/nav_map_pb2.py deleted file mode 100644 index b5b2feb..0000000 --- a/anki_vector/messaging/nav_map_pb2.py +++ /dev/null @@ -1,312 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/nav_map.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -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/nav_map.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - serialized_pb=_b('\n#anki_vector/messaging/nav_map.proto\x12\x1e\x41nki.Vector.external_interface\"x\n\x0eNavMapQuadInfo\x12\x43\n\x07\x63ontent\x18\x01 \x01(\x0e\x32\x32.Anki.Vector.external_interface.NavNodeContentType\x12\r\n\x05\x64\x65pth\x18\x02 \x01(\r\x12\x12\n\ncolor_rgba\x18\x03 \x01(\r\"{\n\nNavMapInfo\x12\x12\n\nroot_depth\x18\x01 \x01(\x05\x12\x14\n\x0croot_size_mm\x18\x02 \x01(\x02\x12\x15\n\rroot_center_x\x18\x03 \x01(\x02\x12\x15\n\rroot_center_y\x18\x04 \x01(\x02\x12\x15\n\rroot_center_z\x18\x05 \x01(\x02\"&\n\x11NavMapFeedRequest\x12\x11\n\tfrequency\x18\x01 \x01(\x02\"\xa9\x01\n\x12NavMapFeedResponse\x12\x11\n\torigin_id\x18\x01 \x01(\r\x12<\n\x08map_info\x18\x02 \x01(\x0b\x32*.Anki.Vector.external_interface.NavMapInfo\x12\x42\n\nquad_infos\x18\x03 \x03(\x0b\x32..Anki.Vector.external_interface.NavMapQuadInfo*\xc8\x02\n\x12NavNodeContentType\x12\x14\n\x10NAV_NODE_UNKNOWN\x10\x00\x12\x1e\n\x1aNAV_NODE_CLEAR_OF_OBSTACLE\x10\x01\x12\x1b\n\x17NAV_NODE_CLEAR_OF_CLIFF\x10\x02\x12\x1a\n\x16NAV_NODE_OBSTACLE_CUBE\x10\x03\x12\x1f\n\x1bNAV_NODE_OBSTACLE_PROXIMITY\x10\x04\x12(\n$NAV_NODE_OBSTACLE_PROXIMITY_EXPLORED\x10\x05\x12\"\n\x1eNAV_NODE_OBSTACLE_UNRECOGNIZED\x10\x06\x12\x12\n\x0eNAV_NODE_CLIFF\x10\x07\x12\x1d\n\x19NAV_NODE_INTERESTING_EDGE\x10\x08\x12!\n\x1dNAV_NODE_NON_INTERESTING_EDGE\x10\tb\x06proto3') -) - -_NAVNODECONTENTTYPE = _descriptor.EnumDescriptor( - name='NavNodeContentType', - full_name='Anki.Vector.external_interface.NavNodeContentType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='NAV_NODE_UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAV_NODE_CLEAR_OF_OBSTACLE', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAV_NODE_CLEAR_OF_CLIFF', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAV_NODE_OBSTACLE_CUBE', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAV_NODE_OBSTACLE_PROXIMITY', index=4, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAV_NODE_OBSTACLE_PROXIMITY_EXPLORED', index=5, number=5, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAV_NODE_OBSTACLE_UNRECOGNIZED', index=6, number=6, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAV_NODE_CLIFF', index=7, number=7, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAV_NODE_INTERESTING_EDGE', index=8, number=8, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NAV_NODE_NON_INTERESTING_EDGE', index=9, number=9, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=531, - serialized_end=859, -) -_sym_db.RegisterEnumDescriptor(_NAVNODECONTENTTYPE) - -NavNodeContentType = enum_type_wrapper.EnumTypeWrapper(_NAVNODECONTENTTYPE) -NAV_NODE_UNKNOWN = 0 -NAV_NODE_CLEAR_OF_OBSTACLE = 1 -NAV_NODE_CLEAR_OF_CLIFF = 2 -NAV_NODE_OBSTACLE_CUBE = 3 -NAV_NODE_OBSTACLE_PROXIMITY = 4 -NAV_NODE_OBSTACLE_PROXIMITY_EXPLORED = 5 -NAV_NODE_OBSTACLE_UNRECOGNIZED = 6 -NAV_NODE_CLIFF = 7 -NAV_NODE_INTERESTING_EDGE = 8 -NAV_NODE_NON_INTERESTING_EDGE = 9 - - - -_NAVMAPQUADINFO = _descriptor.Descriptor( - name='NavMapQuadInfo', - full_name='Anki.Vector.external_interface.NavMapQuadInfo', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='content', full_name='Anki.Vector.external_interface.NavMapQuadInfo.content', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='depth', full_name='Anki.Vector.external_interface.NavMapQuadInfo.depth', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='color_rgba', full_name='Anki.Vector.external_interface.NavMapQuadInfo.color_rgba', index=2, - number=3, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=71, - serialized_end=191, -) - - -_NAVMAPINFO = _descriptor.Descriptor( - name='NavMapInfo', - full_name='Anki.Vector.external_interface.NavMapInfo', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='root_depth', full_name='Anki.Vector.external_interface.NavMapInfo.root_depth', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='root_size_mm', full_name='Anki.Vector.external_interface.NavMapInfo.root_size_mm', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='root_center_x', full_name='Anki.Vector.external_interface.NavMapInfo.root_center_x', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='root_center_y', full_name='Anki.Vector.external_interface.NavMapInfo.root_center_y', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='root_center_z', full_name='Anki.Vector.external_interface.NavMapInfo.root_center_z', index=4, - number=5, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=193, - serialized_end=316, -) - - -_NAVMAPFEEDREQUEST = _descriptor.Descriptor( - name='NavMapFeedRequest', - full_name='Anki.Vector.external_interface.NavMapFeedRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='frequency', full_name='Anki.Vector.external_interface.NavMapFeedRequest.frequency', index=0, - number=1, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=318, - serialized_end=356, -) - - -_NAVMAPFEEDRESPONSE = _descriptor.Descriptor( - name='NavMapFeedResponse', - full_name='Anki.Vector.external_interface.NavMapFeedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='origin_id', full_name='Anki.Vector.external_interface.NavMapFeedResponse.origin_id', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='map_info', full_name='Anki.Vector.external_interface.NavMapFeedResponse.map_info', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='quad_infos', full_name='Anki.Vector.external_interface.NavMapFeedResponse.quad_infos', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=359, - serialized_end=528, -) - -_NAVMAPQUADINFO.fields_by_name['content'].enum_type = _NAVNODECONTENTTYPE -_NAVMAPFEEDRESPONSE.fields_by_name['map_info'].message_type = _NAVMAPINFO -_NAVMAPFEEDRESPONSE.fields_by_name['quad_infos'].message_type = _NAVMAPQUADINFO -DESCRIPTOR.message_types_by_name['NavMapQuadInfo'] = _NAVMAPQUADINFO -DESCRIPTOR.message_types_by_name['NavMapInfo'] = _NAVMAPINFO -DESCRIPTOR.message_types_by_name['NavMapFeedRequest'] = _NAVMAPFEEDREQUEST -DESCRIPTOR.message_types_by_name['NavMapFeedResponse'] = _NAVMAPFEEDRESPONSE -DESCRIPTOR.enum_types_by_name['NavNodeContentType'] = _NAVNODECONTENTTYPE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -NavMapQuadInfo = _reflection.GeneratedProtocolMessageType('NavMapQuadInfo', (_message.Message,), dict( - DESCRIPTOR = _NAVMAPQUADINFO, - __module__ = 'anki_vector.messaging.nav_map_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.NavMapQuadInfo) - )) -_sym_db.RegisterMessage(NavMapQuadInfo) - -NavMapInfo = _reflection.GeneratedProtocolMessageType('NavMapInfo', (_message.Message,), dict( - DESCRIPTOR = _NAVMAPINFO, - __module__ = 'anki_vector.messaging.nav_map_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.NavMapInfo) - )) -_sym_db.RegisterMessage(NavMapInfo) - -NavMapFeedRequest = _reflection.GeneratedProtocolMessageType('NavMapFeedRequest', (_message.Message,), dict( - DESCRIPTOR = _NAVMAPFEEDREQUEST, - __module__ = 'anki_vector.messaging.nav_map_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.NavMapFeedRequest) - )) -_sym_db.RegisterMessage(NavMapFeedRequest) - -NavMapFeedResponse = _reflection.GeneratedProtocolMessageType('NavMapFeedResponse', (_message.Message,), dict( - DESCRIPTOR = _NAVMAPFEEDRESPONSE, - __module__ = 'anki_vector.messaging.nav_map_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.NavMapFeedResponse) - )) -_sym_db.RegisterMessage(NavMapFeedResponse) - - -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/nav_map_pb2_grpc.py b/anki_vector/messaging/nav_map_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/nav_map_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/protocol.py b/anki_vector/messaging/protocol.py deleted file mode 100644 index 2d60fa0..0000000 --- a/anki_vector/messaging/protocol.py +++ /dev/null @@ -1,33 +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. - -# pylint: skip-file - -""" -Protobuf messages exposed to the Vector Python SDK -""" -import sys -import inspect - -from .alexa_pb2 import * -from .behavior_pb2 import * -from .cube_pb2 import * -from .messages_pb2 import * -from .nav_map_pb2 import * -from .response_status_pb2 import * -from .settings_pb2 import * -from .shared_pb2 import * -from .external_interface_pb2 import * - -__all__ = [obj.__name__ for _, obj in inspect.getmembers(sys.modules[__name__]) if inspect.isclass(obj)] diff --git a/anki_vector/messaging/response_status.proto b/anki_vector/messaging/response_status.proto deleted file mode 100644 index 3ac0fc3..0000000 --- a/anki_vector/messaging/response_status.proto +++ /dev/null @@ -1,32 +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. - -// Response status - -syntax = "proto3"; - -package Anki.Vector.external_interface; - -message ResponseStatus { - enum StatusCode { - UNKNOWN = 0; - RESPONSE_RECEIVED = 1; // The message has completed as expected - REQUEST_PROCESSING = 2; // The message has been sent to the robot - OK = 3; - FORBIDDEN = 100; // The user was not authorizied - NOT_FOUND = 101; // The requested attribute was not found - ERROR_UPDATE_IN_PROGRESS = 102; // Currently updating values from another call - } - StatusCode code = 1; -} diff --git a/anki_vector/messaging/response_status_pb2.py b/anki_vector/messaging/response_status_pb2.py deleted file mode 100644 index dcb49e5..0000000 --- a/anki_vector/messaging/response_status_pb2.py +++ /dev/null @@ -1,114 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/response_status.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/response_status.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - serialized_pb=_b('\n+anki_vector/messaging/response_status.proto\x12\x1e\x41nki.Vector.external_interface\"\xe8\x01\n\x0eResponseStatus\x12G\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x39.Anki.Vector.external_interface.ResponseStatus.StatusCode\"\x8c\x01\n\nStatusCode\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x15\n\x11RESPONSE_RECEIVED\x10\x01\x12\x16\n\x12REQUEST_PROCESSING\x10\x02\x12\x06\n\x02OK\x10\x03\x12\r\n\tFORBIDDEN\x10\x64\x12\r\n\tNOT_FOUND\x10\x65\x12\x1c\n\x18\x45RROR_UPDATE_IN_PROGRESS\x10\x66\x62\x06proto3') -) - - - -_RESPONSESTATUS_STATUSCODE = _descriptor.EnumDescriptor( - name='StatusCode', - full_name='Anki.Vector.external_interface.ResponseStatus.StatusCode', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RESPONSE_RECEIVED', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='REQUEST_PROCESSING', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OK', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FORBIDDEN', index=4, number=100, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_FOUND', index=5, number=101, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ERROR_UPDATE_IN_PROGRESS', index=6, number=102, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=172, - serialized_end=312, -) -_sym_db.RegisterEnumDescriptor(_RESPONSESTATUS_STATUSCODE) - - -_RESPONSESTATUS = _descriptor.Descriptor( - name='ResponseStatus', - full_name='Anki.Vector.external_interface.ResponseStatus', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='code', full_name='Anki.Vector.external_interface.ResponseStatus.code', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _RESPONSESTATUS_STATUSCODE, - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=80, - serialized_end=312, -) - -_RESPONSESTATUS.fields_by_name['code'].enum_type = _RESPONSESTATUS_STATUSCODE -_RESPONSESTATUS_STATUSCODE.containing_type = _RESPONSESTATUS -DESCRIPTOR.message_types_by_name['ResponseStatus'] = _RESPONSESTATUS -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -ResponseStatus = _reflection.GeneratedProtocolMessageType('ResponseStatus', (_message.Message,), dict( - DESCRIPTOR = _RESPONSESTATUS, - __module__ = 'anki_vector.messaging.response_status_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ResponseStatus) - )) -_sym_db.RegisterMessage(ResponseStatus) - - -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/response_status_pb2_grpc.py b/anki_vector/messaging/response_status_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/response_status_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 deleted file mode 100644 index d3d35cf..0000000 --- a/anki_vector/messaging/settings.proto +++ /dev/null @@ -1,35 +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. - -// Settings definition - -syntax = "proto3"; - -package Anki.Vector.external_interface; - - -enum ApiVersion { - INVALID = 0; - LATEST = 1; -} - -enum Volume { - MUTE = 0; - LOW = 1; - MEDIUM_LOW = 2; - MEDIUM = 3; - MEDIUM_HIGH = 4; - HIGH = 5; -} - diff --git a/anki_vector/messaging/settings_pb2.py b/anki_vector/messaging/settings_pb2.py deleted file mode 100644 index f5dff10..0000000 --- a/anki_vector/messaging/settings_pb2.py +++ /dev/null @@ -1,103 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/settings.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -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/settings.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - 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', - full_name='Anki.Vector.external_interface.ApiVersion', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='INVALID', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='LATEST', index=1, number=1, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=72, - serialized_end=109, -) -_sym_db.RegisterEnumDescriptor(_APIVERSION) - -ApiVersion = enum_type_wrapper.EnumTypeWrapper(_APIVERSION) -_VOLUME = _descriptor.EnumDescriptor( - name='Volume', - full_name='Anki.Vector.external_interface.Volume', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='MUTE', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='LOW', index=1, number=1, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MEDIUM_LOW', index=2, number=2, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MEDIUM', index=3, number=3, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MEDIUM_HIGH', index=4, number=4, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='HIGH', index=5, number=5, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=111, - serialized_end=193, -) -_sym_db.RegisterEnumDescriptor(_VOLUME) - -Volume = enum_type_wrapper.EnumTypeWrapper(_VOLUME) -INVALID = 0 -LATEST = 1 -MUTE = 0 -LOW = 1 -MEDIUM_LOW = 2 -MEDIUM = 3 -MEDIUM_HIGH = 4 -HIGH = 5 - - -DESCRIPTOR.enum_types_by_name['ApiVersion'] = _APIVERSION -DESCRIPTOR.enum_types_by_name['Volume'] = _VOLUME -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/settings_pb2_grpc.py b/anki_vector/messaging/settings_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/settings_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/shared.proto b/anki_vector/messaging/shared.proto deleted file mode 100644 index f8e130a..0000000 --- a/anki_vector/messaging/shared.proto +++ /dev/null @@ -1,98 +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. - -// External interface for robot <-> app and robot <-> sdk communication - -syntax = "proto3"; - -package Anki.Vector.external_interface; - -import "anki_vector/messaging/behavior.proto"; -import "anki_vector/messaging/cube.proto"; -import "anki_vector/messaging/messages.proto"; -import "anki_vector/messaging/settings.proto"; -import "anki_vector/messaging/extensions.proto"; -import "anki_vector/messaging/response_status.proto"; - -message ProtocolVersionRequest { - int64 client_version = 1; - int64 min_host_version = 2; -} - -message ProtocolVersionResponse { - enum Result { - UNSUPPORTED = 0; - SUCCESS = 1; - } - Result result = 1; - int64 host_version = 2; -} - -message ConnectionResponse { - ResponseStatus status = 1; - bool is_primary = 2; -} - -// Messages originating from the engine -message Event { - oneof event_type { - TimeStampedStatus time_stamped_status = 1; - WakeWord wake_word = 3; - RobotObservedFace robot_observed_face = 5; - RobotChangedObservedFaceID robot_changed_observed_face_id = 6; - ObjectEvent object_event = 7; - StimulationInfo stimulation_info = 8; - PhotoTaken photo_taken = 9; - RobotState robot_state = 10; - CubeBattery cube_battery = 11; - KeepAlivePing keep_alive = 12; - ConnectionResponse connection_response = 13; - MirrorModeDisabled mirror_mode_disabled = 16; - VisionModesAutoDisabled vision_modes_auto_disabled = 17; - } -} - -message FilterList { - repeated string list = 1; -} - -message EventRequest { - oneof list_type { - FilterList white_list = 1; - FilterList black_list = 2; - } - string connection_id = 3; -} - -message EventResponse { - option (streamed) = true; - ResponseStatus status = 1; - Event event = 2; -} - -message UserAuthenticationRequest { - bytes user_session_id = 1; - bytes client_name = 2; -} - -message UserAuthenticationResponse { - ResponseStatus status = 1; - enum Code { - UNAUTHORIZED = 0; - AUTHORIZED = 1; - } - Code code = 2; - bytes client_token_guid = 3; -} - diff --git a/anki_vector/messaging/shared_pb2.py b/anki_vector/messaging/shared_pb2.py deleted file mode 100644 index 0310af8..0000000 --- a/anki_vector/messaging/shared_pb2.py +++ /dev/null @@ -1,657 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: anki_vector/messaging/shared.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() - - -from anki_vector.messaging import behavior_pb2 as anki__vector_dot_messaging_dot_behavior__pb2 -from anki_vector.messaging import cube_pb2 as anki__vector_dot_messaging_dot_cube__pb2 -from anki_vector.messaging import messages_pb2 as anki__vector_dot_messaging_dot_messages__pb2 -from anki_vector.messaging import settings_pb2 as anki__vector_dot_messaging_dot_settings__pb2 -from anki_vector.messaging import extensions_pb2 as anki__vector_dot_messaging_dot_extensions__pb2 -from anki_vector.messaging import response_status_pb2 as anki__vector_dot_messaging_dot_response__status__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='anki_vector/messaging/shared.proto', - package='Anki.Vector.external_interface', - syntax='proto3', - serialized_pb=_b('\n\"anki_vector/messaging/shared.proto\x12\x1e\x41nki.Vector.external_interface\x1a$anki_vector/messaging/behavior.proto\x1a anki_vector/messaging/cube.proto\x1a$anki_vector/messaging/messages.proto\x1a$anki_vector/messaging/settings.proto\x1a&anki_vector/messaging/extensions.proto\x1a+anki_vector/messaging/response_status.proto\"J\n\x16ProtocolVersionRequest\x12\x16\n\x0e\x63lient_version\x18\x01 \x01(\x03\x12\x18\n\x10min_host_version\x18\x02 \x01(\x03\"\xa7\x01\n\x17ProtocolVersionResponse\x12N\n\x06result\x18\x01 \x01(\x0e\x32>.Anki.Vector.external_interface.ProtocolVersionResponse.Result\x12\x14\n\x0chost_version\x18\x02 \x01(\x03\"&\n\x06Result\x12\x0f\n\x0bUNSUPPORTED\x10\x00\x12\x0b\n\x07SUCCESS\x10\x01\"h\n\x12\x43onnectionResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x12\n\nis_primary\x18\x02 \x01(\x08\"\x86\x08\n\x05\x45vent\x12P\n\x13time_stamped_status\x18\x01 \x01(\x0b\x32\x31.Anki.Vector.external_interface.TimeStampedStatusH\x00\x12=\n\twake_word\x18\x03 \x01(\x0b\x32(.Anki.Vector.external_interface.WakeWordH\x00\x12P\n\x13robot_observed_face\x18\x05 \x01(\x0b\x32\x31.Anki.Vector.external_interface.RobotObservedFaceH\x00\x12\x64\n\x1erobot_changed_observed_face_id\x18\x06 \x01(\x0b\x32:.Anki.Vector.external_interface.RobotChangedObservedFaceIDH\x00\x12\x43\n\x0cobject_event\x18\x07 \x01(\x0b\x32+.Anki.Vector.external_interface.ObjectEventH\x00\x12K\n\x10stimulation_info\x18\x08 \x01(\x0b\x32/.Anki.Vector.external_interface.StimulationInfoH\x00\x12\x41\n\x0bphoto_taken\x18\t \x01(\x0b\x32*.Anki.Vector.external_interface.PhotoTakenH\x00\x12\x41\n\x0brobot_state\x18\n \x01(\x0b\x32*.Anki.Vector.external_interface.RobotStateH\x00\x12\x43\n\x0c\x63ube_battery\x18\x0b \x01(\x0b\x32+.Anki.Vector.external_interface.CubeBatteryH\x00\x12\x43\n\nkeep_alive\x18\x0c \x01(\x0b\x32-.Anki.Vector.external_interface.KeepAlivePingH\x00\x12Q\n\x13\x63onnection_response\x18\r \x01(\x0b\x32\x32.Anki.Vector.external_interface.ConnectionResponseH\x00\x12R\n\x14mirror_mode_disabled\x18\x10 \x01(\x0b\x32\x32.Anki.Vector.external_interface.MirrorModeDisabledH\x00\x12]\n\x1avision_modes_auto_disabled\x18\x11 \x01(\x0b\x32\x37.Anki.Vector.external_interface.VisionModesAutoDisabledH\x00\x42\x0c\n\nevent_type\"\x1a\n\nFilterList\x12\x0c\n\x04list\x18\x01 \x03(\t\"\xb6\x01\n\x0c\x45ventRequest\x12@\n\nwhite_list\x18\x01 \x01(\x0b\x32*.Anki.Vector.external_interface.FilterListH\x00\x12@\n\nblack_list\x18\x02 \x01(\x0b\x32*.Anki.Vector.external_interface.FilterListH\x00\x12\x15\n\rconnection_id\x18\x03 \x01(\tB\x0b\n\tlist_type\"\x8b\x01\n\rEventResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12\x34\n\x05\x65vent\x18\x02 \x01(\x0b\x32%.Anki.Vector.external_interface.Event:\x04\x80\xa6\x1d\x01\"I\n\x19UserAuthenticationRequest\x12\x17\n\x0fuser_session_id\x18\x01 \x01(\x0c\x12\x13\n\x0b\x63lient_name\x18\x02 \x01(\x0c\"\xf0\x01\n\x1aUserAuthenticationResponse\x12>\n\x06status\x18\x01 \x01(\x0b\x32..Anki.Vector.external_interface.ResponseStatus\x12M\n\x04\x63ode\x18\x02 \x01(\x0e\x32?.Anki.Vector.external_interface.UserAuthenticationResponse.Code\x12\x19\n\x11\x63lient_token_guid\x18\x03 \x01(\x0c\"(\n\x04\x43ode\x12\x10\n\x0cUNAUTHORIZED\x10\x00\x12\x0e\n\nAUTHORIZED\x10\x01\x62\x06proto3') - , - dependencies=[anki__vector_dot_messaging_dot_behavior__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_cube__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_messages__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_settings__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_extensions__pb2.DESCRIPTOR,anki__vector_dot_messaging_dot_response__status__pb2.DESCRIPTOR,]) - - - -_PROTOCOLVERSIONRESPONSE_RESULT = _descriptor.EnumDescriptor( - name='Result', - full_name='Anki.Vector.external_interface.ProtocolVersionResponse.Result', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNSUPPORTED', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SUCCESS', index=1, number=1, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=509, - serialized_end=547, -) -_sym_db.RegisterEnumDescriptor(_PROTOCOLVERSIONRESPONSE_RESULT) - -_USERAUTHENTICATIONRESPONSE_CODE = _descriptor.EnumDescriptor( - name='Code', - full_name='Anki.Vector.external_interface.UserAuthenticationResponse.Code', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNAUTHORIZED', index=0, number=0, - options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='AUTHORIZED', index=1, number=1, - options=None, - type=None), - ], - containing_type=None, - options=None, - serialized_start=2319, - serialized_end=2359, -) -_sym_db.RegisterEnumDescriptor(_USERAUTHENTICATIONRESPONSE_CODE) - - -_PROTOCOLVERSIONREQUEST = _descriptor.Descriptor( - name='ProtocolVersionRequest', - full_name='Anki.Vector.external_interface.ProtocolVersionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='client_version', full_name='Anki.Vector.external_interface.ProtocolVersionRequest.client_version', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='min_host_version', full_name='Anki.Vector.external_interface.ProtocolVersionRequest.min_host_version', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=303, - serialized_end=377, -) - - -_PROTOCOLVERSIONRESPONSE = _descriptor.Descriptor( - name='ProtocolVersionResponse', - full_name='Anki.Vector.external_interface.ProtocolVersionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='Anki.Vector.external_interface.ProtocolVersionResponse.result', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='host_version', full_name='Anki.Vector.external_interface.ProtocolVersionResponse.host_version', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _PROTOCOLVERSIONRESPONSE_RESULT, - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=380, - serialized_end=547, -) - - -_CONNECTIONRESPONSE = _descriptor.Descriptor( - name='ConnectionResponse', - full_name='Anki.Vector.external_interface.ConnectionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.ConnectionResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_primary', full_name='Anki.Vector.external_interface.ConnectionResponse.is_primary', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=549, - serialized_end=653, -) - - -_EVENT = _descriptor.Descriptor( - name='Event', - full_name='Anki.Vector.external_interface.Event', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='time_stamped_status', full_name='Anki.Vector.external_interface.Event.time_stamped_status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='wake_word', full_name='Anki.Vector.external_interface.Event.wake_word', index=1, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='robot_observed_face', full_name='Anki.Vector.external_interface.Event.robot_observed_face', index=2, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='robot_changed_observed_face_id', full_name='Anki.Vector.external_interface.Event.robot_changed_observed_face_id', index=3, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='object_event', full_name='Anki.Vector.external_interface.Event.object_event', index=4, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='stimulation_info', full_name='Anki.Vector.external_interface.Event.stimulation_info', index=5, - number=8, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='photo_taken', full_name='Anki.Vector.external_interface.Event.photo_taken', index=6, - number=9, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='robot_state', full_name='Anki.Vector.external_interface.Event.robot_state', index=7, - number=10, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cube_battery', full_name='Anki.Vector.external_interface.Event.cube_battery', index=8, - number=11, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keep_alive', full_name='Anki.Vector.external_interface.Event.keep_alive', index=9, - number=12, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='connection_response', full_name='Anki.Vector.external_interface.Event.connection_response', index=10, - number=13, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='mirror_mode_disabled', full_name='Anki.Vector.external_interface.Event.mirror_mode_disabled', index=11, - number=16, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='vision_modes_auto_disabled', full_name='Anki.Vector.external_interface.Event.vision_modes_auto_disabled', index=12, - number=17, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - _descriptor.OneofDescriptor( - name='event_type', full_name='Anki.Vector.external_interface.Event.event_type', - index=0, containing_type=None, fields=[]), - ], - serialized_start=656, - serialized_end=1686, -) - - -_FILTERLIST = _descriptor.Descriptor( - name='FilterList', - full_name='Anki.Vector.external_interface.FilterList', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='list', full_name='Anki.Vector.external_interface.FilterList.list', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1688, - serialized_end=1714, -) - - -_EVENTREQUEST = _descriptor.Descriptor( - name='EventRequest', - full_name='Anki.Vector.external_interface.EventRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='white_list', full_name='Anki.Vector.external_interface.EventRequest.white_list', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='black_list', full_name='Anki.Vector.external_interface.EventRequest.black_list', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='connection_id', full_name='Anki.Vector.external_interface.EventRequest.connection_id', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - _descriptor.OneofDescriptor( - name='list_type', full_name='Anki.Vector.external_interface.EventRequest.list_type', - index=0, containing_type=None, fields=[]), - ], - serialized_start=1717, - serialized_end=1899, -) - - -_EVENTRESPONSE = _descriptor.Descriptor( - name='EventResponse', - full_name='Anki.Vector.external_interface.EventResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.EventResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='event', full_name='Anki.Vector.external_interface.EventResponse.event', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\200\246\035\001')), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1902, - serialized_end=2041, -) - - -_USERAUTHENTICATIONREQUEST = _descriptor.Descriptor( - name='UserAuthenticationRequest', - full_name='Anki.Vector.external_interface.UserAuthenticationRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='user_session_id', full_name='Anki.Vector.external_interface.UserAuthenticationRequest.user_session_id', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='client_name', full_name='Anki.Vector.external_interface.UserAuthenticationRequest.client_name', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2043, - serialized_end=2116, -) - - -_USERAUTHENTICATIONRESPONSE = _descriptor.Descriptor( - name='UserAuthenticationResponse', - full_name='Anki.Vector.external_interface.UserAuthenticationResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='Anki.Vector.external_interface.UserAuthenticationResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='code', full_name='Anki.Vector.external_interface.UserAuthenticationResponse.code', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='client_token_guid', full_name='Anki.Vector.external_interface.UserAuthenticationResponse.client_token_guid', index=2, - number=3, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _USERAUTHENTICATIONRESPONSE_CODE, - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2119, - serialized_end=2359, -) - -_PROTOCOLVERSIONRESPONSE.fields_by_name['result'].enum_type = _PROTOCOLVERSIONRESPONSE_RESULT -_PROTOCOLVERSIONRESPONSE_RESULT.containing_type = _PROTOCOLVERSIONRESPONSE -_CONNECTIONRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_EVENT.fields_by_name['time_stamped_status'].message_type = anki__vector_dot_messaging_dot_messages__pb2._TIMESTAMPEDSTATUS -_EVENT.fields_by_name['wake_word'].message_type = anki__vector_dot_messaging_dot_messages__pb2._WAKEWORD -_EVENT.fields_by_name['robot_observed_face'].message_type = anki__vector_dot_messaging_dot_messages__pb2._ROBOTOBSERVEDFACE -_EVENT.fields_by_name['robot_changed_observed_face_id'].message_type = anki__vector_dot_messaging_dot_messages__pb2._ROBOTCHANGEDOBSERVEDFACEID -_EVENT.fields_by_name['object_event'].message_type = anki__vector_dot_messaging_dot_cube__pb2._OBJECTEVENT -_EVENT.fields_by_name['stimulation_info'].message_type = anki__vector_dot_messaging_dot_messages__pb2._STIMULATIONINFO -_EVENT.fields_by_name['photo_taken'].message_type = anki__vector_dot_messaging_dot_messages__pb2._PHOTOTAKEN -_EVENT.fields_by_name['robot_state'].message_type = anki__vector_dot_messaging_dot_messages__pb2._ROBOTSTATE -_EVENT.fields_by_name['cube_battery'].message_type = anki__vector_dot_messaging_dot_messages__pb2._CUBEBATTERY -_EVENT.fields_by_name['keep_alive'].message_type = anki__vector_dot_messaging_dot_messages__pb2._KEEPALIVEPING -_EVENT.fields_by_name['connection_response'].message_type = _CONNECTIONRESPONSE -_EVENT.fields_by_name['mirror_mode_disabled'].message_type = anki__vector_dot_messaging_dot_messages__pb2._MIRRORMODEDISABLED -_EVENT.fields_by_name['vision_modes_auto_disabled'].message_type = anki__vector_dot_messaging_dot_messages__pb2._VISIONMODESAUTODISABLED -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['time_stamped_status']) -_EVENT.fields_by_name['time_stamped_status'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['wake_word']) -_EVENT.fields_by_name['wake_word'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['robot_observed_face']) -_EVENT.fields_by_name['robot_observed_face'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['robot_changed_observed_face_id']) -_EVENT.fields_by_name['robot_changed_observed_face_id'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['object_event']) -_EVENT.fields_by_name['object_event'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['stimulation_info']) -_EVENT.fields_by_name['stimulation_info'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['photo_taken']) -_EVENT.fields_by_name['photo_taken'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['robot_state']) -_EVENT.fields_by_name['robot_state'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['cube_battery']) -_EVENT.fields_by_name['cube_battery'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['keep_alive']) -_EVENT.fields_by_name['keep_alive'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['connection_response']) -_EVENT.fields_by_name['connection_response'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['mirror_mode_disabled']) -_EVENT.fields_by_name['mirror_mode_disabled'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENT.oneofs_by_name['event_type'].fields.append( - _EVENT.fields_by_name['vision_modes_auto_disabled']) -_EVENT.fields_by_name['vision_modes_auto_disabled'].containing_oneof = _EVENT.oneofs_by_name['event_type'] -_EVENTREQUEST.fields_by_name['white_list'].message_type = _FILTERLIST -_EVENTREQUEST.fields_by_name['black_list'].message_type = _FILTERLIST -_EVENTREQUEST.oneofs_by_name['list_type'].fields.append( - _EVENTREQUEST.fields_by_name['white_list']) -_EVENTREQUEST.fields_by_name['white_list'].containing_oneof = _EVENTREQUEST.oneofs_by_name['list_type'] -_EVENTREQUEST.oneofs_by_name['list_type'].fields.append( - _EVENTREQUEST.fields_by_name['black_list']) -_EVENTREQUEST.fields_by_name['black_list'].containing_oneof = _EVENTREQUEST.oneofs_by_name['list_type'] -_EVENTRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_EVENTRESPONSE.fields_by_name['event'].message_type = _EVENT -_USERAUTHENTICATIONRESPONSE.fields_by_name['status'].message_type = anki__vector_dot_messaging_dot_response__status__pb2._RESPONSESTATUS -_USERAUTHENTICATIONRESPONSE.fields_by_name['code'].enum_type = _USERAUTHENTICATIONRESPONSE_CODE -_USERAUTHENTICATIONRESPONSE_CODE.containing_type = _USERAUTHENTICATIONRESPONSE -DESCRIPTOR.message_types_by_name['ProtocolVersionRequest'] = _PROTOCOLVERSIONREQUEST -DESCRIPTOR.message_types_by_name['ProtocolVersionResponse'] = _PROTOCOLVERSIONRESPONSE -DESCRIPTOR.message_types_by_name['ConnectionResponse'] = _CONNECTIONRESPONSE -DESCRIPTOR.message_types_by_name['Event'] = _EVENT -DESCRIPTOR.message_types_by_name['FilterList'] = _FILTERLIST -DESCRIPTOR.message_types_by_name['EventRequest'] = _EVENTREQUEST -DESCRIPTOR.message_types_by_name['EventResponse'] = _EVENTRESPONSE -DESCRIPTOR.message_types_by_name['UserAuthenticationRequest'] = _USERAUTHENTICATIONREQUEST -DESCRIPTOR.message_types_by_name['UserAuthenticationResponse'] = _USERAUTHENTICATIONRESPONSE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -ProtocolVersionRequest = _reflection.GeneratedProtocolMessageType('ProtocolVersionRequest', (_message.Message,), dict( - DESCRIPTOR = _PROTOCOLVERSIONREQUEST, - __module__ = 'anki_vector.messaging.shared_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ProtocolVersionRequest) - )) -_sym_db.RegisterMessage(ProtocolVersionRequest) - -ProtocolVersionResponse = _reflection.GeneratedProtocolMessageType('ProtocolVersionResponse', (_message.Message,), dict( - DESCRIPTOR = _PROTOCOLVERSIONRESPONSE, - __module__ = 'anki_vector.messaging.shared_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ProtocolVersionResponse) - )) -_sym_db.RegisterMessage(ProtocolVersionResponse) - -ConnectionResponse = _reflection.GeneratedProtocolMessageType('ConnectionResponse', (_message.Message,), dict( - DESCRIPTOR = _CONNECTIONRESPONSE, - __module__ = 'anki_vector.messaging.shared_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.ConnectionResponse) - )) -_sym_db.RegisterMessage(ConnectionResponse) - -Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), dict( - DESCRIPTOR = _EVENT, - __module__ = 'anki_vector.messaging.shared_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.Event) - )) -_sym_db.RegisterMessage(Event) - -FilterList = _reflection.GeneratedProtocolMessageType('FilterList', (_message.Message,), dict( - DESCRIPTOR = _FILTERLIST, - __module__ = 'anki_vector.messaging.shared_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.FilterList) - )) -_sym_db.RegisterMessage(FilterList) - -EventRequest = _reflection.GeneratedProtocolMessageType('EventRequest', (_message.Message,), dict( - DESCRIPTOR = _EVENTREQUEST, - __module__ = 'anki_vector.messaging.shared_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EventRequest) - )) -_sym_db.RegisterMessage(EventRequest) - -EventResponse = _reflection.GeneratedProtocolMessageType('EventResponse', (_message.Message,), dict( - DESCRIPTOR = _EVENTRESPONSE, - __module__ = 'anki_vector.messaging.shared_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.EventResponse) - )) -_sym_db.RegisterMessage(EventResponse) - -UserAuthenticationRequest = _reflection.GeneratedProtocolMessageType('UserAuthenticationRequest', (_message.Message,), dict( - DESCRIPTOR = _USERAUTHENTICATIONREQUEST, - __module__ = 'anki_vector.messaging.shared_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.UserAuthenticationRequest) - )) -_sym_db.RegisterMessage(UserAuthenticationRequest) - -UserAuthenticationResponse = _reflection.GeneratedProtocolMessageType('UserAuthenticationResponse', (_message.Message,), dict( - DESCRIPTOR = _USERAUTHENTICATIONRESPONSE, - __module__ = 'anki_vector.messaging.shared_pb2' - # @@protoc_insertion_point(class_scope:Anki.Vector.external_interface.UserAuthenticationResponse) - )) -_sym_db.RegisterMessage(UserAuthenticationResponse) - - -_EVENTRESPONSE.has_options = True -_EVENTRESPONSE._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('\200\246\035\001')) -# @@protoc_insertion_point(module_scope) diff --git a/anki_vector/messaging/shared_pb2_grpc.py b/anki_vector/messaging/shared_pb2_grpc.py deleted file mode 100644 index a894352..0000000 --- a/anki_vector/messaging/shared_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/motors.py b/anki_vector/motors.py deleted file mode 100644 index 0816b6a..0000000 --- a/anki_vector/motors.py +++ /dev/null @@ -1,107 +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 -# -# https://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. - -""" -Control the motors of Vector. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['MotorComponent'] - -from . import connection, util -from .messaging import protocol - - -class MotorComponent(util.Component): - """Controls the low-level motor functions.""" - @connection.on_connection_thread() - async def set_wheel_motors(self, - left_wheel_speed: float, - right_wheel_speed: float, - left_wheel_accel: float = 0.0, - right_wheel_accel: float = 0.0): - """Tell Vector to move his wheels / treads at a given speed. - - The wheels will continue to move at that speed until commanded to drive - at a new speed. - - To unlock the wheel track, call `set_wheel_motors(0, 0)`. - - .. testcode:: - - import anki_vector - import time - - with anki_vector.Robot() as robot: - robot.motors.set_wheel_motors(25, 50) - time.sleep(3.0) - - :param left_wheel_speed: Speed of the left tread (in millimeters per second). - :param right_wheel_speed: Speed of the right tread (in millimeters per second). - :param left_wheel_accel: Acceleration of left tread (in millimeters per second squared) - ``None`` value defaults this to the same as l_wheel_speed. - :param right_wheel_accel: Acceleration of right tread (in millimeters per second squared) - ``None`` value defaults this to the same as r_wheel_speed. - """ - motors = protocol.DriveWheelsRequest(left_wheel_mmps=left_wheel_speed, - right_wheel_mmps=right_wheel_speed, - left_wheel_mmps2=left_wheel_accel, - right_wheel_mmps2=right_wheel_accel) - return await self.grpc_interface.DriveWheels(motors) - - @connection.on_connection_thread() - async def set_head_motor(self, - speed: float): - """Tell Vector's head motor to move with a certain speed. - - Positive speed for up, negative speed for down. Measured in radians per second. - - To unlock the head track, call `set_head_motor(0)`. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.motors.set_head_motor(-5.0) - - :param speed: Motor speed for Vector's head, measured in radians per second. - """ - set_head_request = protocol.MoveHeadRequest(speed_rad_per_sec=speed) - return await self.grpc_interface.MoveHead(set_head_request) - - @connection.on_connection_thread() - async def set_lift_motor(self, - speed: float): - """Tell Vector's lift motor to move with a certain speed. - - Positive speed for up, negative speed for down. Measured in radians per second. - - To unlock the lift track, call `set_lift_motor(0)`. - - .. testcode:: - - import anki_vector - import time - - with anki_vector.Robot() as robot: - robot.motors.set_lift_motor(-5.0) - time.sleep(3.0) - robot.motors.set_lift_motor(5.0) - time.sleep(3.0) - - :param speed: Motor speed for Vector's lift, measured in radians per second. - """ - set_lift_request = protocol.MoveLiftRequest(speed_rad_per_sec=speed) - return await self.grpc_interface.MoveLift(set_lift_request) diff --git a/anki_vector/nav_map.py b/anki_vector/nav_map.py deleted file mode 100755 index 3db4c34..0000000 --- a/anki_vector/nav_map.py +++ /dev/null @@ -1,405 +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 -# -# https://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. - -"""A 2D navigation memory map of the world around Vector. - -Vector builds a memory map of the navigable world around him as he drives -around. This is mostly based on where objects are seen (the cubes, charger, and -any custom objects), and also includes where Vector detects cliffs/drops, and -probabilities of occupancy, but instead encodes what type of content is there. - -To use the map you must first call :meth:`anki_vector.nav_map.NavMapComponent.init_nav_map_feed` -with a positive frequency so that the data is streamed to the SDK. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['EvtNavMapUpdate', - 'NavMapComponent', 'NavMapGrid', 'NavMapGridNode', - 'NavNodeContentTypes'] - -import asyncio -from concurrent.futures import CancelledError -from enum import Enum -from logging import Logger -from typing import List - -from . import util -from .events import Events -from .messaging import protocol - - -class EvtNavMapUpdate(): # pylint: disable=too-few-public-methods - """Dispatched when a new nav map is received. - - :param nav_map: The current state of the robot's nav map. - """ - - def __init__(self, nav_map): - self.nav_map = nav_map - - -class NavNodeContentTypes(Enum): # pylint: disable=too-few-public-methods - """The content types for a :class:`NavMapGridNode`. - """ - - #: The contents of the node is unknown. - Unknown = protocol.NavNodeContentType.Value("NAV_NODE_UNKNOWN") - - #: The node is clear of obstacles, because Vector has seen objects on the - #: other side, but it might contain a cliff. The node will be marked as - #: either :attr:`Cliff` or :attr:`ClearOfCliff` once Vector has driven there. - ClearOfObstacle = protocol.NavNodeContentType.Value("NAV_NODE_CLEAR_OF_OBSTACLE") - - #: The node is clear of any cliffs (a sharp drop) or obstacles. - ClearOfCliff = protocol.NavNodeContentType.Value("NAV_NODE_CLEAR_OF_CLIFF") - - #: The node contains a :class:`~anki_vector.objects.LightCube`. - ObstacleCube = protocol.NavNodeContentType.Value("NAV_NODE_OBSTACLE_CUBE") - - #: The node contains a proximity detected obstacle which has not been explored. - ObstacleProximity = protocol.NavNodeContentType.Value("NAV_NODE_OBSTACLE_PROXIMITY") - - #: The node contains a proximity detected obstacle which has been explored. - ObstacleProximityExplored = protocol.NavNodeContentType.Value("NAV_NODE_OBSTACLE_PROXIMITY_EXPLORED") - - #: The node contains an unrecognized obstacle. - ObstacleUnrecognized = protocol.NavNodeContentType.Value("NAV_NODE_OBSTACLE_UNRECOGNIZED") - - #: The node contains a cliff (a sharp drop). - Cliff = protocol.NavNodeContentType.Value("NAV_NODE_CLIFF") - - #: The node contains a visible edge (based on the camera feed). - InterestingEdge = protocol.NavNodeContentType.Value("NAV_NODE_INTERESTING_EDGE") - - # This entry is undocumented and not currently used - NonInterestingEdge = protocol.NavNodeContentType.Value("NAV_NODE_NON_INTERESTING_EDGE") - - -class NavMapGridNode: - """A node in a :class:`NavMapGrid`. - - Leaf nodes contain content, all other nodes are split into 4 equally sized - children. - - Child node indices are stored in the following X,Y orientation: - - +---+----+---+ - | ^ | 2 | 0 | - +---+----+---+ - | Y | 3 | 1 | - +---+----+---+ - | | X->| | - +---+----+---+ - """ - - def __init__(self, depth: int, size: float, center: util.Vector3, parent: 'NavMapGridNode', logger: Logger): - #: The depth of this node (i.e. how far down the quad-tree it is). - self.depth = depth - - #: The size (width or length) of this square node. - self.size = size - - #: The center of this node. - self.center = center - - #: The parent of this node. Is ``None`` for the root node. - self.parent = parent - - #: ``None`` for leaf nodes, a list of 4 child nodes otherwise. - self.children: List[NavMapGridNode] = None - - #: The content type in this node. Only leaf nodes have content, - #: this is ``None`` for all other nodes. - self.content: protocol.NavNodeContentType = None - - self._next_child = 0 # Used when building to track which branch to follow - - self._logger = logger - - def __repr__(self): - return '<%s center: %s size: %s content: %s>' % ( - self.__class__.__name__, self.center, self.size, self.content) - - def contains_point(self, x: float, y: float) -> bool: - """Test if the node contains the given x,y coordinates. - - :param x: x coordinate for the point. - :param y: y coordinate for the point. - - Returns: - True if the node contains the point, False otherwise. - """ - half_size = self.size * 0.5 - dist_x = abs(self.center.x - x) - dist_y = abs(self.center.y - y) - return (dist_x <= half_size) and (dist_y <= half_size) - - def _get_node(self, x: float, y: float, assumed_in_bounds: bool) -> 'NavMapGridNode': - if not assumed_in_bounds and not self.contains_point(x, y): - # point is out of bounds - return None - - if self.children is None: - return self - - x_offset = 2 if x < self.center.x else 0 - y_offset = 1 if y < self.center.y else 0 - child_node = self.children[x_offset + y_offset] - # child node is by definition in bounds / on boundary - return child_node._get_node(x, y, True) # pylint: disable=protected-access - - def get_node(self, x: float, y: float) -> 'NavMapGridNode': - """Get the node at the given x,y coordinates. - - :param x: x coordinate for the point. - :param y: y coordinate for the point. - - Returns: - The smallest node that includes the point. - Will be ``None`` if the point is outside of the map. - """ - return self._get_node(x, y, assumed_in_bounds=False) - - def get_content(self, x: float, y: float) -> protocol.NavNodeContentType: - """Get the node's content at the given x,y coordinates. - - :param x: x coordinate for the point. - :param y: y coordinate for the point. - - Returns: - The content included at that point. Will be :attr:`NavNodeContentTypes.Unknown` - if the point is outside of the map. - """ - node = self.get_node(x, y) - if node: - return node.content - - return NavNodeContentTypes.Unknown - - def add_child(self, content: protocol.NavNodeContentType, depth: int) -> bool: - """Add a child node to the quad tree. - - The quad-tree is serialized to a flat list of nodes, we deserialize - back to a quad-tree structure here, with the depth of each node - indicating where it is placed. - - :param content: The content to store in the leaf node. - :param depth: The depth that this leaf node is located at. - - Returns: - True if parent should use the next child for future add_child - calls. - """ - if depth > self.depth: - self._logger.error("NavMapGridNode depth %s > %s", depth, self.depth) - if self._next_child > 3: - self._logger.error("NavMapGridNode _next_child %s (>3) at depth %s", self._next_child, self.depth) - - if self.depth == depth: - if self.content is not None: - self._logger.error("NavMapGridNode: Clobbering %s at depth %s with %s", - self.content, self.depth, content) - self.content = content - # This node won't be further subdivided, and is now full - return True - - if self.children is None: - # Create 4 child nodes for quad-tree structure - next_depth = self.depth - 1 - next_size = self.size * 0.5 - offset = next_size * 0.5 - center1 = util.Vector3(self.center.x + offset, self.center.y + offset, self.center.z) - center2 = util.Vector3(self.center.x + offset, self.center.y - offset, self.center.z) - center3 = util.Vector3(self.center.x - offset, self.center.y + offset, self.center.z) - center4 = util.Vector3(self.center.x - offset, self.center.y - offset, self.center.z) - self.children = [NavMapGridNode(next_depth, next_size, center1, self, self._logger), - NavMapGridNode(next_depth, next_size, center2, self, self._logger), - NavMapGridNode(next_depth, next_size, center3, self, self._logger), - NavMapGridNode(next_depth, next_size, center4, self, self._logger)] - if self.children[self._next_child].add_child(content, depth): - # Child node is now full, start using the next child - self._next_child += 1 - - if self._next_child > 3: - # All children are now full - parent should start using the next child - return True - - # Empty children remain - parent can keep using this child - return False - - -class NavMapGrid: - """A navigation memory map, stored as a quad-tree.""" - - def __init__(self, msg: protocol.NavMapFeedResponse, logger: Logger): - #: The origin ID for the map. Only maps and :class:`~anki_vector.util.Pose` - #: objects of the same origin ID are in the same coordinate frame and - #: can therefore be compared. - self.origin_id = msg.origin_id - root_center = util.Vector3(msg.map_info.root_center_x, msg.map_info.root_center_y, msg.map_info.root_center_z) - self._root_node = NavMapGridNode(msg.map_info.root_depth, msg.map_info.root_size_mm, root_center, None, logger) - for quad in msg.quad_infos: - self.add_quad(quad.content, quad.depth) - - self._logger = logger - - def __repr__(self): - return '<%s center: %s size: %s>' % ( - self.__class__.__name__, self.center, self.size) - - @property - def root_node(self) -> NavMapGridNode: - """The root node for the grid, contains all other nodes.""" - return self._root_node - - @property - def size(self) -> float: - """The size (width or length) of the square grid.""" - return self._root_node.size - - @property - def center(self) -> util.Vector3: - """The center of this map.""" - return self._root_node.center - - def contains_point(self, x: float, y: float) -> bool: - """Test if the map contains the given x,y coordinates. - - :param x: x coordinate for the point. - :param y: y coordinate for the point. - - Returns: - True if the map contains the point, False otherwise. - """ - return self._root_node.contains_point(x, y) - - def get_node(self, x: float, y: float) -> NavMapGridNode: - """Get the node at the given x,y coordinates. - - :param x: x coordinate for the point. - :param y: y coordinate for the point. - - Returns: - The smallest node that includes the point. - Will be ``None`` if the point is outside of the map. - """ - return self._root_node.get_node(x, y) - - def get_content(self, x: float, y: float) -> protocol.NavNodeContentType: - """Get the map's content at the given x,y coordinates. - - :param x: x coordinate for the point. - :param y: y coordinate for the point. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_nav_map_feed=True) as robot: - # Make sure Vector drives around so the nav map will update - robot.behavior.drive_off_charger() - robot.motors.set_wheel_motors(-100, 100) - latest_nav_map = robot.nav_map.latest_nav_map - content = latest_nav_map.get_content(0.0, 100.0) - print(f"Sampling point at 0.0, 100.0 and found content: {content}") - - Returns: - The content included at that point. Will be :attr:`NavNodeContentTypes.Unknown` - if the point is outside of the map. - """ - return self._root_node.get_content(x, y) - - def add_quad(self, content: protocol.NavNodeContentType, depth: int): - """Adds a new quad to the nav map. - - :param content: What content this node contains. - :param depth: How deep in the navMap this node is. - """ - self._root_node.add_child(content, depth) - - -class NavMapComponent(util.Component): - """Represents Vector's navigation memory map. - - The NavMapComponent object subscribes for nav memory map updates from the robot to store and dispatch. - - The :class:`anki_vector.robot.Robot` or :class:`anki_vector.robot.AsyncRobot` instance hosts this component. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_nav_map_feed=True) as robot: - # Make sure Vector drives around so the nav map will update - robot.behavior.drive_off_charger() - robot.motors.set_wheel_motors(-100, 100) - latest_nav_map = robot.nav_map.latest_nav_map - - :param robot: A reference to the owner Robot object. - """ - - def __init__(self, robot): - super().__init__(robot) - - self._latest_nav_map: NavMapGrid = None - self._nav_map_feed_task: asyncio.Task = None - - @property - @util.block_while_none() - def latest_nav_map(self) -> NavMapGrid: - """:class:`NavMapGrid`: The most recently processed image received from the robot. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_nav_map_feed=True) as robot: - # Make sure Vector drives around so the nav map will update - robot.behavior.drive_off_charger() - robot.motors.set_wheel_motors(-100, 100) - latest_nav_map = robot.nav_map.latest_nav_map - """ - if not self._nav_map_feed_task or self._nav_map_feed_task.done(): - raise Exception("Nav map not initialized") - return self._latest_nav_map - - def init_nav_map_feed(self, frequency: float = 0.5) -> None: - """Begin nav map feed task. - - :param frequency: How frequently to send nav map updates. - """ - if not self._nav_map_feed_task or self._nav_map_feed_task.done(): - self._nav_map_feed_task = self.conn.loop.create_task(self._request_and_handle_nav_maps(frequency)) - - def close_nav_map_feed(self) -> None: - """Cancel nav map feed task.""" - if self._nav_map_feed_task: - self._nav_map_feed_task.cancel() - future = self.conn.run_coroutine(self._nav_map_feed_task) - future.result() - self._nav_map_feed_task = None - - async def _request_and_handle_nav_maps(self, frequency: float) -> None: - """Queries and listens for nav map feed events from the robot. - Received events are parsed by a helper function. - - :param frequency: How frequently to send nav map updates. - """ - try: - req = protocol.NavMapFeedRequest(frequency=frequency) - async for evt in self.grpc_interface.NavMapFeed(req): - self._latest_nav_map = NavMapGrid(evt, self.logger) - await self._robot.events.dispatch_event(evt, Events.nav_map_update) - except CancelledError: - self.logger.debug('Nav Map feed task was cancelled. This is expected during disconnection.') diff --git a/anki_vector/objects.py b/anki_vector/objects.py deleted file mode 100644 index 4888df0..0000000 --- a/anki_vector/objects.py +++ /dev/null @@ -1,1658 +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 -# -# https://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. - -"""Object and Light Cube recognition. - -Vector can recognize and track a number of different types of objects. - -These objects may be visible (currently observed by the robot's camera) -and tappable (in the case of the Light Cube that ships with the robot). - -The Light Cube is known as a :class:`LightCube` by the SDK. The cube -has controllable lights, and sensors that can determine when it's being -moved or tapped. - -Objects can generate events which can be subscribed to from the anki_vector.events -class, such as object_appeared (of type EvtObjectAppeared), and -object_disappeared (of type EvtObjectDisappeared), which are broadcast -based on both robot originating events and local state. - -All observable objects have a marker of a known size attached to them, which allows Vector -to recognize the object and its position and rotation ("pose"). You can attach -markers to your own objects for Vector to recognize by printing them out from the -online documentation. They will be detected as :class:`CustomObject` instances. - -Vector connects to his Light Cubes with BLE. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['LIGHT_CUBE_1_TYPE', 'OBJECT_VISIBILITY_TIMEOUT', - 'EvtObjectAppeared', 'EvtObjectDisappeared', 'EvtObjectFinishedMove', 'EvtObjectObserved', - 'Charger', 'CustomObjectArchetype', 'CustomObject', 'CustomObjectMarkers', 'CustomObjectTypes', - 'FixedCustomObject', 'LightCube', 'ObservableObject'] - -# TODO Curious why events like the following aren't listed? At least some do seem to be supported in other parts of anki_vector. -# EvtObjectTapped, EvtObjectConnectChanged, EvtObjectConnected, EvtObjectLocated, EvtObjectMoving, EvtObjectMovingStarted - - -import collections -import math -import time - -from . import connection, lights, util -from .events import Events - -from .messaging import protocol - -#: Length of time in seconds to go without receiving an observed event before -#: assuming that Vector can no longer see an object. -OBJECT_VISIBILITY_TIMEOUT = 0.8 - - -class EvtObjectObserved(): # pylint: disable=too-few-public-methods - """Triggered whenever an object is visually identified by the robot. - - A stream of these events are produced while an object is visible to the robot. - Each event has an updated image_box field. - - See EvtObjectAppeared if you only want to know when an object first - becomes visible. - - :param obj: The object that was observed - :param image_rect: An :class:`anki_vector.util.ImageRect`: defining where the object is within Vector's camera view - :param pose: The :class:`anki_vector.util.Pose`: defining the position and rotation of the object - """ - - def __init__(self, obj, image_rect: util.ImageRect, pose: util.Pose): - self.obj = obj - self.image_rect = image_rect - self.pose = pose - - -class EvtObjectAppeared(): # pylint: disable=too-few-public-methods - """Triggered whenever an object is first visually identified by a robot. - - This differs from EvtObjectObserved in that it's only triggered when - an object initially becomes visible. If it disappears for more than - OBJECT_VISIBILITY_TIMEOUT seconds and then is seen again, a - EvtObjectDisappeared will be dispatched, followed by another - EvtObjectAppeared event. - - For continuous tracking information about a visible object, see - EvtObjectObserved. - - :param obj: The object that is starting to be observed - :param image_rect: An :class:`anki_vector.util.ImageRect`: defining where the object is within Vector's camera view - :param pose: The :class:`anki_vector.util.Pose`: defining the position and rotation of the object - """ - - def __init__(self, obj, image_rect: util.ImageRect, pose: util.Pose): - self.obj = obj - self.image_rect = image_rect - self.pose = pose - - -class EvtObjectDisappeared(): # pylint: disable=too-few-public-methods - """Triggered whenever an object that was previously being observed is no longer visible. - - :param obj: The object that is no longer being observed - """ - - def __init__(self, obj): - self.obj = obj - - -class EvtObjectFinishedMove(): # pylint: disable=too-few-public-methods - """Triggered when an active object stops moving. - - :param obj: The object that moved - :param move_duration: The duration of the move - """ - - def __init__(self, obj, move_duration: float): - self.obj = obj - self.move_duration = move_duration - - -class ObservableObject(util.Component): - """Represents any object Vector can see in the world.""" - - visibility_timeout = OBJECT_VISIBILITY_TIMEOUT - - def __init__(self, robot, **kw): - super().__init__(robot, **kw) - - self._pose: util.Pose = None - - #: The time the last event was received. - #: ``None`` if no events have yet been received. - self._last_event_time: float = None - - #: The time the element was last observed by the robot. - #: ``None`` if the element has not yet been observed. - self._last_observed_time: float = None - - #: The robot's timestamp of the last observed event. - #: ``None`` if the element has not yet been observed. - #: In milliseconds relative to robot epoch. - self._last_observed_robot_timestamp: int = None - - #: The ImageRect defining where the - #: object was last visible within Vector's camera view. - #: ``None`` if the element has not yet been observed. - self._last_observed_image_rect: util.ImageRect = None - - self._is_visible: bool = False - self._observed_timeout_handler: callable = None - - def __repr__(self): - extra = self._repr_values() - if extra: - extra = ' ' + extra - if self.pose: - extra += ' pose=%s' % self.pose - - return '<%s%s is_visible=%s>' % (self.__class__.__name__, - extra, self.is_visible) - #### Properties #### - - @property - def pose(self) -> util.Pose: - """The pose of this object in the world. - - Is ``None`` for elements that don't have pose information. - - .. testcode:: - - import anki_vector - import time - - # First, place a cube directly in front of Vector so he can observe it. - - with anki_vector.Robot() as robot: - connectionResult = robot.world.connect_cube() - connected_cube = robot.world.connected_light_cube - - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(connected_cube) - print("last observed timestamp: " + str(connected_cube.last_observed_time) + ", robot timestamp: " + str(connected_cube.last_observed_robot_timestamp)) - print(robot.world.connected_light_cube.pose) - time.sleep(0.5) - """ - return self._pose - - @property - def last_event_time(self) -> float: - """Time this object last received an event from Vector.""" - return self._last_event_time - - @property - def last_observed_time(self) -> float: - """Time this object was last seen.""" - return self._last_observed_time - - @property - def last_observed_robot_timestamp(self) -> int: - """Time this object was last seen according to Vector's time.""" - return self._last_observed_robot_timestamp - - @property - def time_since_last_seen(self) -> float: - """Time since this object was last seen. math.inf if never seen. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_face_detection=True) as robot: - for face in robot.world.visible_faces: - print(f"time_since_last_seen: {face.time_since_last_seen}") - """ - if self._last_observed_time is None: - return math.inf - return time.time() - self._last_observed_time - - @property - def last_observed_image_rect(self) -> util.ImageRect: - """The location in 2d camera space where this object was last seen.""" - return self._last_observed_image_rect - - @property - def is_visible(self) -> bool: - """True if the element has been observed recently, False otherwise. - - "recently" is defined as :attr:`visibility_timeout` seconds. - """ - return self._is_visible - - #### Private Methods #### - - def _repr_values(self): # pylint: disable=no-self-use - return '' - - def _reset_observed_timeout_handler(self): - if self._observed_timeout_handler is not None: - self._observed_timeout_handler.cancel() - self._observed_timeout_handler = self.conn.loop.call_later(self.visibility_timeout, self._observed_timeout) - - def _observed_timeout(self): - # Triggered when the element is no longer considered "visible". - # i.e. visibility_timeout seconds after the last observed event. - self._is_visible = False - self.conn.run_soon(self._robot.events.dispatch_event(EvtObjectDisappeared(self), Events.object_disappeared)) - - def _on_observed(self, pose: util.Pose, image_rect: util.ImageRect, robot_timestamp: int): - # Called from subclasses on their corresponding observed messages. - newly_visible = self._is_visible is False - self._is_visible = True - - now = time.time() - self._last_observed_time = now - self._last_observed_robot_timestamp = robot_timestamp - self._last_event_time = now - self._last_observed_image_rect = image_rect - self._pose = pose - self._reset_observed_timeout_handler() - self.conn.run_soon(self._robot.events.dispatch_event(EvtObjectObserved(self, image_rect, pose), Events.object_observed)) - - if newly_visible: - self.conn.run_soon(self._robot.events.dispatch_event(EvtObjectAppeared(self, image_rect, pose), Events.object_appeared)) - - -#: LIGHT_CUBE_1_TYPE's markers look like 2 concentric circles with lines and gaps. -LIGHT_CUBE_1_TYPE = protocol.ObjectType.Value("BLOCK_LIGHTCUBE1") - - -class LightCube(ObservableObject): - """Represents Vector's Cube. - - The LightCube object has four LEDs that Vector can actively manipulate and communicate with. - - As Vector drives around, he uses the position of objects that he recognizes, including his cube, - to localize himself, taking note of the :class:`anki_vector.util.Pose` of the objects. - - You can subscribe to cube events including :class:`anki_vector.events.Events.object_tapped`, - :class:`anki_vector.events.Events.object_appeared`, and :class:`anki_vector.events.Events.object_disappeared`. - - Vector supports 1 LightCube. - - See parent class :class:`ObservableObject` for additional properties - and methods. - """ - - #: Length of time in seconds to go without receiving an observed event before - #: assuming that Vector can no longer see an object. Can be overridden in subclasses. - visibility_timeout = OBJECT_VISIBILITY_TIMEOUT - - def __init__(self, robot, **kw): - super().__init__(robot, **kw) - - #: The time the object was last tapped. - #: ``None`` if the cube wasn't tapped yet. - self._last_tapped_time: float = None - - #: The robot's timestamp of the last tapped event. - #: ``None`` if the cube wasn't tapped yet. - #: In milliseconds relative to robot epoch. - self._last_tapped_robot_timestamp: int = None - - #: The time the object was last moved. - #: ``None`` if the cube wasn't moved yet. - self._last_moved_time: float = None - - #: The robot's timestamp of the last move event. - #: ``None`` if the cube wasn't moved yet. - #: In milliseconds relative to robot epoch. - self._last_moved_robot_timestamp: int = None - - #: The time the object started moving when last moved. - self._last_moved_start_time: float = None - - #: The robot's timestamp of when the object started moving when last moved. - #: ``None`` if the cube wasn't moved yet. - #: In milliseconds relative to robot epoch. - self._last_moved_start_robot_timestamp: int = None - - #: The time the last up axis event was received. - #: ``None`` if no events have yet been received. - self._last_up_axis_changed_time: float = None - - #: The robot's timestamp of the last up axis event. - #: ``None`` if the there has not been an up axis event. - #: In milliseconds relative to robot epoch. - self._last_up_axis_changed_robot_timestamp: int = None - - # The object's up_axis value from the last time it changed. - self._up_axis: protocol.UpAxis = None - - #: True if the cube's accelerometer indicates that the cube is moving. - self._is_moving: bool = False - - #: True if the cube is currently connected to the robot via radio. - self._is_connected: bool = False - - #: angular distance from the current reported up axis - #: ``None`` if the object has not yet been observed. - self._top_face_orientation_rad: float = None - - self._object_id: str = None - - #: unique identification of the physical cube - self._factory_id: str = None - - #: Subscribe to relevant events - self.robot.events.subscribe( - self._on_object_connection_state_changed, - Events.object_connection_state) - - self.robot.events.subscribe( - self._on_object_moved, - Events.object_moved) - - self.robot.events.subscribe( - self._on_object_stopped_moving, - Events.object_stopped_moving) - - self.robot.events.subscribe( - self._on_object_up_axis_changed, - Events.object_up_axis_changed) - - self.robot.events.subscribe( - self._on_object_tapped, - Events.object_tapped) - - self.robot.events.subscribe( - self._on_object_observed, - Events.robot_observed_object) - - self.robot.events.subscribe( - self._on_object_connection_lost, - Events.cube_connection_lost) - - #### Public Methods #### - - def teardown(self): - """All faces will be torn down by the world when no longer needed.""" - self.robot.events.unsubscribe( - self._on_object_connection_state_changed, - Events.object_connection_state) - - self.robot.events.unsubscribe( - self._on_object_moved, - Events.object_moved) - - self.robot.events.unsubscribe( - self._on_object_stopped_moving, - Events.object_stopped_moving) - - self.robot.events.unsubscribe( - self._on_object_up_axis_changed, - Events.object_up_axis_changed) - - self.robot.events.unsubscribe( - self._on_object_tapped, - Events.object_tapped) - - self.robot.events.unsubscribe( - self._on_object_observed, - Events.robot_observed_object) - - self.robot.events.unsubscribe( - self._on_object_connection_lost, - Events.cube_connection_lost) - - @connection.on_connection_thread() - async def set_light_corners(self, - light1: lights.Light, - light2: lights.Light, - light3: lights.Light, - light4: lights.Light, - color_profile: lights.ColorProfile = lights.WHITE_BALANCED_CUBE_PROFILE): - """Set the light for each corner. - - .. testcode:: - - import anki_vector - - import time - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - - if robot.world.connected_light_cube: - cube = robot.world.connected_light_cube - - cube.set_light_corners(anki_vector.lights.blue_light, - anki_vector.lights.green_light, - anki_vector.lights.red_light, - anki_vector.lights.white_light) - time.sleep(3) - - cube.set_lights_off() - - :param light1: The settings for the first light. - :param light2: The settings for the second light. - :param light3: The settings for the third light. - :param light4: The settings for the fourth light. - :param color_profile: The profile to be used for the cube lights - """ - params = lights.package_request_params((light1, light2, light3, light4), color_profile) - req = protocol.SetCubeLightsRequest( - object_id=self._object_id, - on_color=params['on_color'], - off_color=params['off_color'], - on_period_ms=params['on_period_ms'], - off_period_ms=params['off_period_ms'], - transition_on_period_ms=params['transition_on_period_ms'], - transition_off_period_ms=params['transition_off_period_ms'], - offset=[0, 0, 0, 0], - relative_to_x=0.0, - relative_to_y=0.0, - rotate=False, - make_relative=protocol.SetCubeLightsRequest.OFF) # pylint: disable=no-member - return await self.grpc_interface.SetCubeLights(req) - - def set_lights(self, light: lights.Light, color_profile: lights.ColorProfile = lights.WHITE_BALANCED_CUBE_PROFILE): - """Set all lights on the cube - - .. testcode:: - - import anki_vector - - import time - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - - if robot.world.connected_light_cube: - cube = robot.world.connected_light_cube - - # Set cube lights to yellow - cube.set_lights(anki_vector.lights.yellow_light) - time.sleep(3) - - cube.set_lights_off() - - :param light: The settings for the lights - :param color_profile: The profile to be used for the cube lights - """ - return self.set_light_corners(light, light, light, light, color_profile) - - def set_lights_off(self, color_profile: lights.ColorProfile = lights.WHITE_BALANCED_CUBE_PROFILE): - """Set all lights off on the cube - - .. testcode:: - - import anki_vector - - import time - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - - if robot.world.connected_light_cube: - cube = robot.world.connected_light_cube - - # Set cube lights to yellow - cube.set_lights(anki_vector.lights.yellow_light) - time.sleep(3) - - # Turn off cube lights - cube.set_lights_off() - - :param color_profile: The profile to be used for the cube lights - """ - - return self.set_light_corners(lights.off_light, lights.off_light, lights.off_light, lights.off_light, color_profile) - - #### Private Methods #### - - def _repr_values(self): - return 'object_id=%s' % self.object_id - - #### Properties #### - - @property - def last_tapped_time(self) -> float: - """The time the object was last tapped in SDK time. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'last_tapped_time: {connected_cube.last_tapped_time}') - time.sleep(0.5) - """ - return self._last_tapped_time - - @property - def last_tapped_robot_timestamp(self) -> float: - """The time the object was last tapped in robot time. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'last_tapped_robot_timestamp: {connected_cube.last_tapped_robot_timestamp}') - time.sleep(0.5) - """ - return self._last_tapped_robot_timestamp - - @property - def last_moved_time(self) -> float: - """The time the object was last moved in SDK time. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'last_moved_time: {connected_cube.last_moved_time}') - time.sleep(0.5) - """ - return self._last_moved_time - - @property - def last_moved_robot_timestamp(self) -> float: - """The time the object was last moved in robot time. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'last_moved_robot_timestamp: {connected_cube.last_moved_robot_timestamp}') - time.sleep(0.5) - """ - return self._last_moved_robot_timestamp - - @property - def last_moved_start_time(self) -> float: - """The time the object most recently started moving in SDK time. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'last_moved_start_time: {connected_cube.last_moved_start_time}') - time.sleep(0.5) - """ - return self._last_moved_start_time - - @property - def last_moved_start_robot_timestamp(self) -> float: - """The time the object more recently started moving in robot time. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'last_moved_start_robot_timestamp: {connected_cube.last_moved_start_robot_timestamp}') - time.sleep(0.5) - """ - return self._last_moved_start_robot_timestamp - - @property - def last_up_axis_changed_time(self) -> float: - """The time the object's orientation last changed in SDK time. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'last_up_axis_changed_time: {connected_cube.last_up_axis_changed_time}') - time.sleep(0.5) - """ - return self._last_up_axis_changed_time - - @property - def last_up_axis_changed_robot_timestamp(self) -> float: - """Time the object's orientation last changed in robot time. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'last_up_axis_changed_robot_timestamp: {connected_cube.last_up_axis_changed_robot_timestamp}') - time.sleep(0.5) - """ - return self._last_up_axis_changed_robot_timestamp - - @property - def up_axis(self) -> protocol.UpAxis: - """The object's up_axis value from the last time it changed. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'up_axis: {connected_cube.up_axis}') - time.sleep(0.5) - """ - return self._up_axis - - @property - def is_moving(self) -> bool: - """True if the cube's accelerometer indicates that the cube is moving. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'is_moving: {connected_cube.is_moving}') - time.sleep(0.5) - """ - return self._is_moving - - @property - def is_connected(self) -> bool: - """True if the cube is currently connected to the robot. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - if robot.world.connected_light_cube: - cube = robot.world.connected_light_cube - print(f"{cube.is_connected}") - """ - return self._is_connected - - @property - def top_face_orientation_rad(self) -> float: - """Angular distance from the current reported up axis. - - .. testcode:: - - import time - import anki_vector - - with anki_vector.Robot() as robot: - print("disconnecting from any connected cube...") - robot.world.disconnect_cube() - - time.sleep(2) - - print("connect to a cube...") - connectionResult = robot.world.connect_cube() - - print("For the next 8 seconds, please tap and move the cube. Cube properties will be logged to console.") - for _ in range(16): - connected_cube = robot.world.connected_light_cube - if connected_cube: - print(f'top_face_orientation_rad: {connected_cube.top_face_orientation_rad}') - time.sleep(0.5) - """ - return self._top_face_orientation_rad - - @property - def factory_id(self) -> str: - """The unique hardware id of the physical cube. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - if robot.world.connected_light_cube: - cube = robot.world.connected_light_cube - print(f"{cube.factory_id}") - """ - return self._factory_id - - @factory_id.setter - def factory_id(self, value: str): - self._factory_id = value - - @property - def descriptive_name(self) -> str: - """A descriptive name for this ObservableObject instance. - - Note: Sub-classes should override this to add any other relevant info - for that object type. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - if robot.world.connected_light_cube: - cube = robot.world.connected_light_cube - print(f"{cube.descriptive_name}") - """ - return "{0} id={1} factory_id={2} is_connected={3}".format(self.__class__.__name__, self._object_id, self._factory_id, self._is_connected) - - @property - def object_id(self) -> int: - """The internal ID assigned to the object. - - This value can only be assigned once as it is static on the robot. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - if robot.world.connected_light_cube: - cube = robot.world.connected_light_cube - print(f"{cube.object_id}") - """ - return self._object_id - - @object_id.setter - def object_id(self, value: str): - if self._object_id is not None: - # We cannot currently rely on robot ensuring that object ID remains static - # E.g. in the case of a cube disconnecting and reconnecting it's removed - # and then re-added to blockworld which results in a new ID. - self.logger.warning("Changing object_id for %s from %s to %s", self.__class__, self._object_id, value) - else: - self.logger.debug("Setting object_id for %s to %s", self.__class__, value) - self._object_id = value - - #### Private Event Handlers #### - - def _on_object_connection_state_changed(self, _, msg): - if msg.object_type == LIGHT_CUBE_1_TYPE: - self._object_id = msg.object_id - - if self._factory_id != msg.factory_id: - self.logger.debug('Factory id changed from {0} to {1}'.format(self._factory_id, msg.factory_id)) - self._factory_id = msg.factory_id - - if self._is_connected != msg.connected: - if msg.connected: - self.logger.debug('Object connected: %s', self) - else: - self.logger.debug('Object disconnected: %s', self) - self._is_connected = msg.connected - - def _on_object_moved(self, _, msg): - if msg.object_id == self._object_id: - now = time.time() - started_moving = not self._is_moving - self._is_moving = True - self._last_event_time = now - self._last_moved_time = now - self._last_moved_robot_timestamp = msg.timestamp - - if started_moving: - self._last_moved_start_time = now - self._last_moved_start_robot_timestamp = msg.timestamp - else: - self.logger.warning('An object not currently tracked by the world moved with id {0}'.format(msg.object_id)) - - async def _on_object_stopped_moving(self, _, msg): - if msg.object_id == self._object_id: - now = time.time() - self._last_event_time = now - move_duration = 0.0 - - # _is_moving might already be false. - # This happens for very short movements that are immediately - # considered stopped (no acceleration info is present) - if self._is_moving: - self._is_moving = False - move_duration = now - self._last_moved_start_time - await self._robot.events.dispatch_event(EvtObjectFinishedMove(self, move_duration), Events.object_finished_move) - else: - self.logger.warning('An object not currently tracked by the world stopped moving with id {0}'.format(msg.object_id)) - - def _on_object_up_axis_changed(self, _, msg): - if msg.object_id == self._object_id: - - now = time.time() - self._up_axis = msg.up_axis - self._last_event_time = now - self._last_up_axis_changed_time = now - self._last_up_axis_changed_robot_timestamp = msg.timestamp - else: - self.logger.warning('Up Axis changed on an object not currently tracked by the world with id {0}'.format(msg.object_id)) - - def _on_object_tapped(self, _, msg): - if msg.object_id == self._object_id: - - now = time.time() - self._last_event_time = now - self._last_tapped_time = now - self._last_tapped_robot_timestamp = msg.timestamp - else: - self.logger.warning('Tapped an object not currently tracked by the world with id {0}'.format(msg.object_id)) - - def _on_object_observed(self, _, msg): - if msg.object_id == self._object_id: - - pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z, - q0=msg.pose.q0, q1=msg.pose.q1, - q2=msg.pose.q2, q3=msg.pose.q3, - origin_id=msg.pose.origin_id) - image_rect = util.ImageRect(msg.img_rect.x_top_left, - msg.img_rect.y_top_left, - msg.img_rect.width, - msg.img_rect.height) - self._top_face_orientation_rad = msg.top_face_orientation_rad - - self._on_observed(pose, image_rect, msg.timestamp) - - def _on_object_connection_lost(self, _, msg): - if msg.object_id == self._object_id: - self._is_connected = False - - -class Charger(ObservableObject): - """Vector's charger object, which the robot can observe and drive toward. - We get an :class:`anki_vector.objects.EvtObjectObserved` message when the - robot sees the charger. - - See parent class :class:`ObservableObject` for additional properties - and methods. - - .. testcode:: - - import anki_vector - - # Position Vector so he can see his charger - with anki_vector.Robot() as robot: - if robot.world.charger: - print('Robot is aware of charger: {0}'.format(robot.world.charger)) - """ - - def __init__(self, robot, object_id: int, **kw): - super().__init__(robot, **kw) - - self._object_id = object_id - - self.robot.events.subscribe( - self._on_object_observed, - Events.robot_observed_object) - - #### Public Methods #### - - def teardown(self): - """All objects will be torn down by the world when the world closes.""" - - self.robot.events.unsubscribe( - self._on_object_observed, - Events.robot_observed_object) - - #### Properties #### - @property - def object_id(self) -> int: - """The internal ID assigned to the object. - - .. testcode:: - - import anki_vector - - # Position Vector so he can see his charger - with anki_vector.Robot() as robot: - if robot.world.charger: - charger_object_id = robot.world.charger.object_id - print(f"charger_object_id: {charger_object_id}") - - This value can only be assigned once as it is static on the robot. - """ - return self._object_id - - @object_id.setter - def object_id(self, value: str): - if self._object_id is not None: - # We cannot currently rely on robot ensuring that object ID remains static - # E.g. in the case of a cube disconnecting and reconnecting it's removed - # and then re-added to blockworld which results in a new ID. - self.logger.warning("Changing object_id for %s from %s to %s", self.__class__, self._object_id, value) - else: - self.logger.debug("Setting object_id for %s to %s", self.__class__, value) - self._object_id = value - - #### Private Methods #### - - def _on_object_observed(self, _, msg): - if msg.object_id == self._object_id: - - pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z, - q0=msg.pose.q0, q1=msg.pose.q1, - q2=msg.pose.q2, q3=msg.pose.q3, - origin_id=msg.pose.origin_id) - image_rect = util.ImageRect(msg.img_rect.x_top_left, - msg.img_rect.y_top_left, - msg.img_rect.width, - msg.img_rect.height) - - self._on_observed(pose, image_rect, msg.timestamp) - - -class CustomObjectArchetype(): - """An object archetype defined by the SDK. It is bound to a specific objectType e.g ``CustomType00``. - - This defined object is given a size in the x,y and z axis. The dimensions - of the markers on the object are also defined. - - When the robot observes custom objects, they will be linked to these archetypes. - These can be created using the methods - :meth:`~anki_vector.world.World.define_custom_box`, - :meth:`~anki_vector.world.World.define_custom_cube`, or - :meth:`~anki_vector.world.World.define_custom_wall`. - """ - - def __init__(self, - custom_type: protocol.CustomType, - x_size_mm: float, - y_size_mm: float, - z_size_mm: float, - marker_width_mm: float, - marker_height_mm: float, - is_unique: bool): - - self._custom_type = custom_type - self._x_size_mm = x_size_mm - self._y_size_mm = y_size_mm - self._z_size_mm = z_size_mm - self._marker_width_mm = marker_width_mm - self._marker_height_mm = marker_height_mm - self._is_unique = is_unique - - #### Properties #### - - @property - def custom_type(self) -> protocol.CustomType: - """id of this archetype on the robot - - .. testcode:: - - import anki_vector - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - for obj in robot.world.custom_object_archetypes: - print('custom object archetype defined with type: {0}'.format(obj.custom_type)) - """ - return self._custom_type - - @property - def x_size_mm(self) -> float: - """Size of this object in its X axis, in millimeters. - - .. testcode:: - - import anki_vector - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - for obj in robot.world.custom_object_archetypes: - print('custom object archetype defined with dimensions: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm)) - """ - return self._x_size_mm - - @property - def y_size_mm(self) -> float: - """Size of this object in its Y axis, in millimeters. - - .. testcode:: - - import anki_vector - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - for obj in robot.world.custom_object_archetypes: - print('custom object archetype defined with dimensions: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm)) - """ - return self._y_size_mm - - @property - def z_size_mm(self) -> float: - """Size of this object in its Z axis, in millimeters. - - .. testcode:: - - import anki_vector - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - for obj in robot.world.custom_object_archetypes: - print('custom object archetype defined with dimensions: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm)) - """ - return self._z_size_mm - - @property - def marker_width_mm(self) -> float: - """Width in millimeters of the marker on this object. - - .. testcode:: - - import anki_vector - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - for obj in robot.world.custom_object_archetypes: - print('custom object archetype defined with marker size: {0}mm x {1}mm'.format(obj.marker_width_mm, obj.marker_height_mm)) - """ - return self._marker_width_mm - - @property - def marker_height_mm(self) -> float: - """Height in millimeters of the marker on this object. - - .. testcode:: - - import anki_vector - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - for obj in robot.world.custom_object_archetypes: - print('custom object archetype defined with marker size: {0}mm x {1}mm'.format(obj.marker_width_mm, obj.marker_height_mm)) - """ - return self._marker_height_mm - - @property - def is_unique(self) -> bool: - """True if there should only be one of this object type in the world.""" - return self._is_unique - - #### Private Methods #### - - def __repr__(self): - return ('custom_type={self.custom_type} ' - 'x_size_mm={self.x_size_mm:.1f} ' - 'y_size_mm={self.y_size_mm:.1f} ' - 'z_size_mm={self.z_size_mm:.1f} ' - 'marker_width_mm={self.marker_width_mm:.1f} ' - 'marker_height_mm={self.marker_height_mm:.1f} ' - 'is_unique={self.is_unique}'.format(self=self)) - - -class CustomObject(ObservableObject): - """An object defined by the SDK observed by the robot. The object will - reference a :class:`CustomObjectArchetype`, with additional instance data. - - These objects are created automatically by the engine when Vector observes - an object with custom markers. For Vector to see one of these you must first - define an archetype with custom markers, via one of the following methods: - :meth:`~anki_vector.world.World.define_custom_box`. - :meth:`~anki_vector.world.World.define_custom_cube`, or - :meth:`~anki_vector.world.World.define_custom_wall` - """ - - def __init__(self, - robot, - archetype: CustomObjectArchetype, - object_id: int, **kw): - super().__init__(robot, **kw) - - self._object_id = object_id - self._archetype = archetype - - self.robot.events.subscribe( - self._on_object_observed, - Events.robot_observed_object) - - #### Public Methods #### - - def teardown(self): - """All objects will be torn down by the world when no longer needed.""" - - self.robot.events.unsubscribe( - self._on_object_observed, - Events.robot_observed_object) - - #### Properties #### - - @property - def object_id(self) -> int: - """The internal ID assigned to the object. - - This value can only be assigned once as it is static on the robot. - - .. testcode:: - - import anki_vector - from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00, - marker=CustomObjectMarkers.Circles2, - size_mm=20.0, - marker_width_mm=50.0, marker_height_mm=50.0) - - # have the robot observe a custom object in the real world with the Circles2 marker - - for obj in robot.world.visible_custom_objects: - print('custom object seen with id: {0}'.format(obj.object_id)) - """ - return self._object_id - - @object_id.setter - def object_id(self, value: str): - if self._object_id is not None: - # We cannot currently rely on robot ensuring that object ID remains static - # E.g. in the case of a cube disconnecting and reconnecting it's removed - # and then re-added to robot's internal world model which results in a new ID. - self.logger.warning("Changing object_id for %s from %s to %s", self.__class__, self._object_id, value) - else: - self.logger.debug("Setting object_id for %s to %s", self.__class__, value) - self._object_id = value - - @property - def archetype(self) -> CustomObjectArchetype: - """Archetype defining this custom object's properties. - - .. testcode:: - - import anki_vector - from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00, - marker=CustomObjectMarkers.Circles2, - size_mm=20.0, - marker_width_mm=50.0, marker_height_mm=50.0) - - # have the robot observe a custom object in the real world with the Circles2 marker - - for obj in robot.world.visible_custom_objects: - print('custom object seen with archetype: {0}'.format(obj.archetype)) - """ - return self._archetype - - @property - def descriptive_name(self) -> str: - """A descriptive name for this CustomObject instance. - - .. testcode:: - - import anki_vector - from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00, - marker=CustomObjectMarkers.Circles2, - size_mm=20.0, - marker_width_mm=50.0, marker_height_mm=50.0) - - # have the robot observe a custom object in the real world with the Circles2 marker - - for obj in robot.world.visible_custom_objects: - print('custom object seen with name: {0}'.format(obj.descriptive_name)) - """ - # Specialization of ObservableObject's method to include the object type. - return "%s id=%d" % (self.archetype.object_type.name, self.object_id) - - #### Private Methods #### - - def _repr_values(self): - return ('object_type={archetype.custom_type} ' - 'x_size_mm={archetype.x_size_mm:.1f} ' - 'y_size_mm={archetype.y_size_mm:.1f} ' - 'z_size_mm={archetype.z_size_mm:.1f} ' - 'is_unique={archetype.is_unique}'.format(archetype=self._archetype)) - - def _on_object_observed(self, _, msg): - if msg.object_id == self._object_id: - - pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z, - q0=msg.pose.q0, q1=msg.pose.q1, - q2=msg.pose.q2, q3=msg.pose.q3, - origin_id=msg.pose.origin_id) - image_rect = util.ImageRect(msg.img_rect.x_top_left, - msg.img_rect.y_top_left, - msg.img_rect.width, - msg.img_rect.height) - - self._on_observed(pose, image_rect, msg.timestamp) - - -class _CustomObjectType(collections.namedtuple('_CustomObjectType', 'name id')): - # Tuple mapping between Proto CustomObjectType name and ID - # All instances will be members of CustomObjectType - - # Keep _CustomObjectType as lightweight as a normal namedtuple - __slots__ = () - - def __str__(self): - return 'CustomObjectTypes.%s' % self.name - - -class CustomObjectTypes(): # pylint: disable=too-few-public-methods - """Defines all available custom object types. - - For use with world.define_custom methods such as - :meth:`anki_vector.world.World.define_custom_box`, - :meth:`anki_vector.world.World.define_custom_cube`, and - :meth:`anki_vector.world.World.define_custom_wall` - - .. testcode:: - - import anki_vector - from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00, - marker=CustomObjectMarkers.Circles2, - size_mm=20.0, - marker_width_mm=50.0, marker_height_mm=50.0) - """ - - #: CustomType00 - the first custom object type - CustomType00 = _CustomObjectType("CustomType00", protocol.CustomType.Value("CUSTOM_TYPE_00")) - - #: - CustomType01 = _CustomObjectType("CustomType01", protocol.CustomType.Value("CUSTOM_TYPE_01")) - - #: - CustomType02 = _CustomObjectType("CustomType02", protocol.CustomType.Value("CUSTOM_TYPE_02")) - - #: - CustomType03 = _CustomObjectType("CustomType03", protocol.CustomType.Value("CUSTOM_TYPE_03")) - - #: - CustomType04 = _CustomObjectType("CustomType04", protocol.CustomType.Value("CUSTOM_TYPE_04")) - - #: - CustomType05 = _CustomObjectType("CustomType05", protocol.CustomType.Value("CUSTOM_TYPE_05")) - - #: - CustomType06 = _CustomObjectType("CustomType06", protocol.CustomType.Value("CUSTOM_TYPE_06")) - - #: - CustomType07 = _CustomObjectType("CustomType07", protocol.CustomType.Value("CUSTOM_TYPE_07")) - - #: - CustomType08 = _CustomObjectType("CustomType08", protocol.CustomType.Value("CUSTOM_TYPE_08")) - - #: - CustomType09 = _CustomObjectType("CustomType09", protocol.CustomType.Value("CUSTOM_TYPE_09")) - - #: - CustomType10 = _CustomObjectType("CustomType10", protocol.CustomType.Value("CUSTOM_TYPE_10")) - - #: - CustomType11 = _CustomObjectType("CustomType11", protocol.CustomType.Value("CUSTOM_TYPE_11")) - - #: - CustomType12 = _CustomObjectType("CustomType12", protocol.CustomType.Value("CUSTOM_TYPE_12")) - - #: - CustomType13 = _CustomObjectType("CustomType13", protocol.CustomType.Value("CUSTOM_TYPE_13")) - - #: - CustomType14 = _CustomObjectType("CustomType14", protocol.CustomType.Value("CUSTOM_TYPE_14")) - - #: - CustomType15 = _CustomObjectType("CustomType15", protocol.CustomType.Value("CUSTOM_TYPE_15")) - - #: - CustomType16 = _CustomObjectType("CustomType16", protocol.CustomType.Value("CUSTOM_TYPE_16")) - - #: - CustomType17 = _CustomObjectType("CustomType17", protocol.CustomType.Value("CUSTOM_TYPE_17")) - - #: - CustomType18 = _CustomObjectType("CustomType18", protocol.CustomType.Value("CUSTOM_TYPE_18")) - - #: CustomType19 - the last custom object type - CustomType19 = _CustomObjectType("CustomType19", protocol.CustomType.Value("CUSTOM_TYPE_19")) - - -class _CustomObjectMarker(collections.namedtuple('_CustomObjectMarker', 'name id')): - # Tuple mapping between Proto CustomObjectMarker name and ID - # All instances will be members of CustomObjectMarker - - # Keep _CustomObjectMarker as lightweight as a normal namedtuple - __slots__ = () - - def __str__(self): - return 'CustomObjectMarkers.%s' % self.name - - -class CustomObjectMarkers(): # pylint: disable=too-few-public-methods - """Defines all available custom object markers. - - For use with world.define_custom methods such as - :meth:`anki_vector.world.World.define_custom_box`, - :meth:`anki_vector.world.World.define_custom_cube`, and - :meth:`anki_vector.world.World.define_custom_wall` - - .. testcode:: - - import anki_vector - from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00, - marker=CustomObjectMarkers.Circles2, - size_mm=20.0, - marker_width_mm=50.0, marker_height_mm=50.0) - """ - - #: .. image:: ../images/custom_markers/SDK_2Circles.png - Circles2 = _CustomObjectMarker("Circles2", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_CIRCLES_2")) - - #: .. image:: ../images/custom_markers/SDK_3Circles.png - Circles3 = _CustomObjectMarker("Circles3", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_CIRCLES_3")) - - #: .. image:: ../images/custom_markers/SDK_4Circles.png - Circles4 = _CustomObjectMarker("Circles4", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_CIRCLES_4")) - - #: .. image:: ../images/custom_markers/SDK_5Circles.png - Circles5 = _CustomObjectMarker("Circles5", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_CIRCLES_5")) - - #: .. image:: ../images/custom_markers/SDK_2Diamonds.png - Diamonds2 = _CustomObjectMarker("Diamonds2", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_DIAMONDS_2")) - - #: .. image:: ../images/custom_markers/SDK_3Diamonds.png - Diamonds3 = _CustomObjectMarker("Diamonds3", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_DIAMONDS_3")) - - #: .. image:: ../images/custom_markers/SDK_4Diamonds.png - Diamonds4 = _CustomObjectMarker("Diamonds4", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_DIAMONDS_4")) - - #: .. image:: ../images/custom_markers/SDK_5Diamonds.png - Diamonds5 = _CustomObjectMarker("Diamonds5", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_DIAMONDS_5")) - - #: .. image:: ../images/custom_markers/SDK_2Hexagons.png - Hexagons2 = _CustomObjectMarker("Hexagons2", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_HEXAGONS_2")) - - #: .. image:: ../images/custom_markers/SDK_3Hexagons.png - Hexagons3 = _CustomObjectMarker("Hexagons3", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_HEXAGONS_3")) - - #: .. image:: ../images/custom_markers/SDK_4Hexagons.png - Hexagons4 = _CustomObjectMarker("Hexagons4", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_HEXAGONS_4")) - - #: .. image:: ../images/custom_markers/SDK_5Hexagons.png - Hexagons5 = _CustomObjectMarker("Hexagons5", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_HEXAGONS_5")) - - #: .. image:: ../images/custom_markers/SDK_2Triangles.png - Triangles2 = _CustomObjectMarker("Triangles2", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_TRIANGLES_2")) - - #: .. image:: ../images/custom_markers/SDK_3Triangles.png - Triangles3 = _CustomObjectMarker("Triangles3", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_TRIANGLES_3")) - - #: .. image:: ../images/custom_markers/SDK_4Triangles.png - Triangles4 = _CustomObjectMarker("Triangles4", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_TRIANGLES_4")) - - #: .. image:: ../images/custom_markers/SDK_5Triangles.png - Triangles5 = _CustomObjectMarker("Triangles5", protocol.CustomObjectMarker.Value("CUSTOM_MARKER_TRIANGLES_5")) - - -class FixedCustomObject(util.Component): - """A fixed object defined by the SDK. It is given a pose and x,y,z sizes. - - This object cannot be observed by the robot so its pose never changes. - The position is static in Vector's world view; once instantiated, these - objects never move. This could be used to make Vector aware of objects and - know to plot a path around them even when they don't have any markers. - - To create these use :meth:`~anki_vector.world.World.create_custom_fixed_object` - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, Pose - import time - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)), - 10, 100, 100, relative_to_robot=True) - """ - - def __init__(self, - robot, - pose: util.Pose, - x_size_mm: float, - y_size_mm: float, - z_size_mm: float, - object_id: int, **kw): - super().__init__(robot, **kw) - self._pose = pose - self._x_size_mm = x_size_mm - self._y_size_mm = y_size_mm - self._z_size_mm = z_size_mm - self._object_id = object_id - - def __repr__(self): - return ('<%s pose=%s object_id=%d x_size_mm=%.1f y_size_mm=%.1f z_size_mm=%.1f=>' % - (self.__class__.__name__, self.pose, self.object_id, - self.x_size_mm, self.y_size_mm, self.z_size_mm)) - - #### Public Methods #### - - def teardown(self): - pass - - #### Properties #### - @property - def object_id(self) -> int: - """The internal ID assigned to the object. - - This value can only be assigned once as it is static in the engine. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, Pose - import time - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)), - 10, 100, 100, relative_to_robot=True) - print('fixed custom object id: {0}'.format(obj.object_id)) - """ - return self._object_id - - @object_id.setter - def object_id(self, value: int): - if self._object_id is not None: - raise ValueError("Cannot change object ID once set (from %s to %s)" % (self._object_id, value)) - self.logger.debug("Updated object_id for %s from %s to %s", self.__class__, self._object_id, value) - self._object_id = value - - @property - def pose(self) -> util.Pose: - """The pose of the object in the world. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, Pose - import time - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)), - 10, 100, 100, relative_to_robot=True) - print('fixed custom object id: {0}'.format(obj.pose)) - """ - return self._pose - - @property - def x_size_mm(self) -> float: - """The length of the object in its X axis, in millimeters. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, Pose - import time - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)), - 10, 100, 100, relative_to_robot=True) - print('fixed custom object size: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm)) - """ - return self._x_size_mm - - @property - def y_size_mm(self) -> float: - """The length of the object in its Y axis, in millimeters. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, Pose - import time - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)), - 10, 100, 100, relative_to_robot=True) - print('fixed custom object size: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm)) - """ - return self._y_size_mm - - @property - def z_size_mm(self) -> float: - """The length of the object in its Z axis, in millimeters. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, Pose - import time - - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - obj = robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)), - 10, 100, 100, relative_to_robot=True) - print('fixed custom object size: {0}mm x {1}mm x {2}mm'.format(obj.x_size_mm, obj.y_size_mm, obj.z_size_mm)) - """ - return self._z_size_mm diff --git a/anki_vector/opengl/__init__.py b/anki_vector/opengl/__init__.py deleted file mode 100644 index 0db3dc8..0000000 --- a/anki_vector/opengl/__init__.py +++ /dev/null @@ -1,100 +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. - -"""This module provides 3D classes for running the OpenGL Viewer. - -It should be launched in a separate process to allow Vector to run freely while -the viewer is rendering. - -It uses PyOpenGL, a Python OpenGL 3D graphics library which is available on most -platforms. It also depends on the Pillow library for image processing. - -Warning: - This package requires Python to have the PyOpenGL package installed, along - with an implementation of GLUT (OpenGL Utility Toolkit). - - 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). - - On Linux: ``sudo apt-get install freeglut3`` - - On Windows: Go to http://freeglut.sourceforge.net/ to get a ``freeglut.dll`` - file. It's included in any of the `Windows binaries` downloads. Place the DLL - next to your Python script, or install it somewhere in your PATH to allow any - script to use it." -""" - -import multiprocessing as mp - -from . import opengl_viewer - - -def main(close_event: mp.Event, - input_intent_queue: mp.Queue, - nav_map_queue: mp.Queue, - world_frame_queue: mp.Queue, - extra_render_function_queue: mp.Queue, - user_data_queue: mp.Queue): - """Run the 3D Viewer window. This is intended to run on a background process. - - .. code-block:: python - - import multiprocessing as mp - - from anki_vector import opengl - - ctx = mp.get_context('spawn') - close_event = ctx.Event() - input_intent_queue = ctx.Queue(maxsize=10) - nav_map_queue = ctx.Queue(maxsize=10) - world_frame_queue = ctx.Queue(maxsize=10) - extra_render_function_queue = ctx.Queue(maxsize=1) - user_data_queue = ctx.Queue() - process = ctx.Process(target=opengl.main, - args=(close_event, - input_intent_queue, - nav_map_queue, - world_frame_queue, - extra_render_function_queue, - user_data_queue), - daemon=True) - process.start() - - :param close_event: Used to notify each process when done rendering. - :type close_event: multiprocessing.Event - :param input_intent_queue: Sends key commands from the 3D viewer process to the main process. - :type input_intent_queue: multiprocessing.Queue - :param nav_map_queue: Updates the 3D viewer process with the latest navigation map. - :type nav_map_queue: multiprocessing.Queue - :param world_frame_queue: Provides the 3D viewer with details about the world. - :type world_frame_queue: multiprocessing.Queue - :param extra_render_function_queue: Functions to be executed in the 3D viewer process. - :type extra_render_function_queue: multiprocessing.Queue - :param user_data_queue: A queue that may be used outside the SDK to pass information to the viewer process. - May be used by ``extra_render_function_queue`` functions. - """ - viewer = opengl_viewer.OpenGLViewer(close_event, - input_intent_queue, - nav_map_queue, - world_frame_queue, - extra_render_function_queue, - user_data_queue) - viewer.run() - - -__all__ = ['main'] diff --git a/anki_vector/opengl/assets/LICENSE.txt b/anki_vector/opengl/assets/LICENSE.txt deleted file mode 100644 index 26ecd40..0000000 --- a/anki_vector/opengl/assets/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -Anki, Inc. Image and 3D Model License Agreement Version 1.1 (last updated November 15, 2018) - -This Image and 3D Model License Agreement (this "Agreement") governs the terms and conditions of your access to and use of Licensed Materials (as defined below), and is made between you, as an individual or entity ("you"), and Anki, Inc. ("we," "us" or "Licensor"). You accept and agree to be bound by this Agreement by your access to or use of any of the Licensed Materials. - -The "Licensed Materials" are the digital images, and 3D models, that we make available to you from time to time in connection with this Agreement. - -1. License. Subject to the terms and conditions of this Agreement, we hereby grant you a limited, revocable, worldwide, fully-paid, royalty free, non-exclusive, non-transferable copyright license during the term of this Agreement to access, copy, display, perform, modify the size of, and distribute, in any of the Licensed Materials, in each case: (A) solely in connection with your use of the Vector SDK in accordance with our separate SDK license agreement(s) or the applicable Anki hardware products (e.g. Vector) and/or the Vector App, and (B) only provided that you comply with the Anki Terms of Use at www.anki.com/terms and any other terms that may apply to the Vector device and/or Vector mobile application and that we may from time to time modify. Licensee may not sublicense any of the foregoing rights, except for the right to access, copy, display perform and distribute the Licensed Materials only in connection with the SDK in an app created by the Licensee. For clarity, this license does not include the right to commercially distribute the Licensed Materials in print form. - -2. Reservation. Licensor (or its suppliers) owns and retains all right, title, and interest in and to each of the Licensed Materials worldwide including, but not limited to, ownership of all copyrights and other intellectual property rights therein. We reserve all rights not explicitly licensed in this Agreement. - -3. DISCLAIMER OF WARRANTY AND LIMITATION OF LIABILITY. THE LICENSED MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NONINFRINGEMENT. IN NO EVENT WILL LICENSOR BE LIABLE TO YOU OR TO ANY THIRD PARTY FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, PUNITIVE OR CONSEQUENTIAL DAMAGES, OR LOST REVENUE, SAVINGS OR PROFITS, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE) OR OTHERWISE, ARISING FROM OR IN CONNECTION WITH ANY OF THE LICENSED MATERIALS, WHETHER OR NOT THAT PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -4. Indemnification. You will defend, indemnify and hold harmless Licensor and its officers, directors, shareholders, employees, and agents from any loss, liability, cost or expense, including attorneys' fees ("Liabilities") that arises from any claim, action or proceeding in connection with your use of the Licensed Materials or your breach of this Agreement. You shall have control of the defense and all related settlement negotiations for such claim, action or proceeding; provided that we shall have the right to consent to any settlement or entry of judgment, such consent not to be unreasonably withheld, and we may participate in such defense using our own counsel at our own expense. - -5. Termination. You may terminate this Agreement at any time by deleting or destroying all copies of the Licensed Materials that you possess or control. We may terminate this Agreement and/or your license to any or all of the Licensed Materials at any time without prior notice to you. In case of termination, you must cease all access and use of, and delete or destroy, all copies of the Licensed Materials that you possess or control. Sections 2 through 8 of this Agreement will survive termination of this Agreement. - -6. Modifications to this Agreement and Licensed Materials. We may amend this Agreement at any time by posting an amended version online and/or sending information regarding the amendment to your email address of record with us. You shall be deemed to have accepted such amendments by continuing to access and/or use any Licensed Materials after such amendments have been posted or information regarding such amendments has been sent to you. If you do not agree to any of such changes, you may terminate this Agreement and immediately cease all access to and use of Licensed Materials. You agree that such termination will be your exclusive remedy in such event. No other waiver or modification of this Agreement shall be valid unless in writing and signed by both parties. We also reserve the right at any time and from time to time to modify or discontinue all or any portion of any Licensed Materials without notice to you. We shall not be liable to you or any third party should we exercise such rights. - -7. Assignment. You may not assign this Agreement, in whole or in part, without our prior written consent, and any attempt by you to assign this Agreement without such consent shall be void. Subject to the foregoing, this Agreement shall benefit and bind both parties, and their successors and permitted assigns. - -8. General. You shall comply with all laws, rules and regulations applicable to your activities under this Agreement. This Agreement shall be governed by and construed in accordance with the laws of the State of California, U.S.A., except for its conflicts of laws principles. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. This Agreement will not be construed to create or imply any partnership, agency or joint venture between the parties. If any provision of this Agreement is found illegal or unenforceable, it will be enforced to the maximum extent permissible, and the legality and enforceability of the other provisions of this Agreement will not be affected. This Agreement is the complete agreement between the parties with respect to its subject matter, and supersedes any prior agreements and communications (both written and oral) regarding such subject matter. diff --git a/anki_vector/opengl/assets/cube.jpg b/anki_vector/opengl/assets/cube.jpg deleted file mode 100644 index 244708b6caa21014ad0783778379397459824da1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33358 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 str: - """Key used for identifying this context.""" - return self._context_id - - def load(self, *args: str): - """Loads a resource given a groups of key arguments. - - Since the resource_stream path is non_stantard with os.path.join, this resolve is encapsulated inside - the resource manager. The context that these resources are files on disk is similarly encapsulated. - All client classes only need match keys with returned data. - - :param args: string keys for identifying the asset. - """ - resource_path = '/'.join(map(str, args)) # Note: Deliberately not os.path.join, for use with pkg_resources - return resource_stream(self._context_id, resource_path) - - -class DynamicTexture: - """Wrapper around An OpenGL Texture that can be dynamically updated.""" - - def __init__(self): - self._texId = glGenTextures(1) - self._width = None - self._height = None - # Bind an ID for this texture - glBindTexture(GL_TEXTURE_2D, self._texId) - # Use bilinear filtering if the texture has to be scaled - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) - - def bind(self): - """Bind the texture for rendering.""" - glBindTexture(GL_TEXTURE_2D, self._texId) - - def update(self, pil_image: Image.Image): - """Update the texture to contain the provided image. - - :param pil_image: The image to write into the texture. - """ - # Ensure the image is in RGBA format and convert to the raw RGBA bytes. - image_width, image_height = pil_image.size - image = pil_image.convert("RGBA").tobytes("raw", "RGBA") - - # Bind the texture so that it can be modified. - self.bind() - if (self._width == image_width) and (self._height == image_height): - # Same size - just need to update the texels. - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_width, image_height, - GL_RGBA, GL_UNSIGNED_BYTE, image) - else: - # Different size than the last frame (e.g. the Window is resizing) - # Create a new texture of the correct size. - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, image) - - self._width = image_width - self._height = image_height - - -class MaterialLibrary(): # pylint: disable=too-few-public-methods - """Load a .mtl material asset, and return the contents as a dictionary. - - Supports the subset of MTL required for the Vector 3D Viewer assets. - - :param resource_manager: Manager to load resources from. - :param asset_key: The key of the asset to load. - """ - - def __init__(self, resource_context: ResourceManager, asset_key: str): - self._contents = {} - current_mtl: dict = None - - file_data = resource_context.load('assets', asset_key) - - for line in file_data: - line = line.decode("utf-8") # Convert bytes line to a string - if line.startswith('#'): - # Ignore comments in the file. - continue - - values = line.split() - if not values: - # Ignore empty lines. - continue - - attribute_name = values[0] - if attribute_name == 'newmtl': - # Create a new empty material. - current_mtl = self._contents[values[1]] = {} - elif current_mtl is None: - raise ValueError("mtl file must start with newmtl statement") - elif attribute_name == 'map_Kd': - # Diffuse texture map - load the image into memory. - image_name = values[1] - image_file_data = resource_context.load('assets', image_name) - with Image.open(image_file_data) as image: - image_width, image_height = image.size - image = image.convert("RGBA").tobytes("raw", "RGBA") - - # Bind the image as a texture that can be used for rendering. - texture_id = glGenTextures(1) - current_mtl['texture_Kd'] = texture_id # pylint: disable=unsupported-assignment-operation - - glBindTexture(GL_TEXTURE_2D, texture_id) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, image) - else: - # Store the values for this attribute as a list of float values. - current_mtl[attribute_name] = list(map(float, values[1:])) # pylint: disable=unsupported-assignment-operation - - def get_material_by_name(self, name: str) -> dict: - """Returns a dict of material attributes""" - return self._contents[name] - - -class MeshFace(): - """A face polygon in 3d space - the basic building block of 3D models. - - The actual coordinate data is stored in tables on the host mesh, the face contains - indexes to those table denoting which values should be used in rendering. - - :param position_ids: Worldspace position ids on the host mesh for this face's vertices. - :param normal_ids: Normal direction ids on the host mesh for this face's vertices. - :param tex_ids: Texture coordinate ids on the host mesh for this face's vertices. - :param active_material: Material name used to render this face. - """ - - def __init__(self, position_ids: List[int], normal_ids: List[int], tex_ids: List[int], active_material: str): - self._position_ids = position_ids - self._normal_ids = normal_ids - self._tex_ids = tex_ids - - self._vertex_count = len(position_ids) - - self._material = active_material - - @property - def position_ids(self) -> List[int]: - """Worldspace position ids on the host mesh for this face's vertices.""" - return self._position_ids - - @property - def normal_ids(self) -> List[int]: - """Normal direction ids on the host mesh for this face's vertices.""" - return self._normal_ids - - @property - def tex_ids(self) -> List[int]: - """Texture coordinate ids on the host mesh for this face's vertices.""" - return self._tex_ids - - @property - def material(self) -> str: - """Material name used to render this face.""" - return self._material - - @property - def vertex_count(self) -> int: - """The number of vertices on this face - will be either 3 for a triangle, or 4 for a quad""" - return self._vertex_count - - -class MeshGroup(): - """A colletions of face polygons which can be rendered as a group by name. - - :param name: The name associated with this part of the 3d mesh collection. - """ - - def __init__(self, name: str): - self._name = name - self._faces: List(MeshFace) = [] - - @property - def name(self) -> str: - """The name associated with the mesh group.""" - return self._name - - @property - def faces(self) -> List[MeshFace]: - """All faces associated with the mesh group.""" - return self._faces - - def add_face_by_obj_data(self, source_values: list, active_material: str): - """Adds a new face to the mesh group. - - Face source data is made up of 3 or 4 vertices - - e.g. `f v1 v2 v3` or `f v1 v2 v3 v4` - - where each vertex definition is multiple indexes seperated by - slashes and can follow the following formats: - - position_index - position_index/tex_coord_index - position_index/tex_coord_index/normal_index - position_index//normal_index - - :param source_values: obj data to parse for this face - :param active_material: the material used to render this face - """ - - position_ids: List(int) = [] - normal_ids: List(int) = [] - tex_ids: List(int) = [] - - for vertex in source_values: - vertex_components = vertex.split('/') - - position_ids.append(int(vertex_components[0])) - - # There's only a texture coordinate if there's at least 2 entries and the 2nd entry is non-zero length - if len(vertex_components) >= 2 and vertex_components[1]: - tex_ids.append(int(vertex_components[1])) - else: - # OBJ file indexing starts at 1, so use 0 to indicate no entry - tex_ids.append(0) - - # There's only a normal if there's at least 2 entries and the 2nd entry is non-zero length - if len(vertex_components) >= 3 and vertex_components[2]: - normal_ids.append(int(vertex_components[2])) - else: - # OBJ file indexing starts at 1, so use 0 to indicate no entry - normal_ids.append(0) - - self._faces.append(MeshFace(position_ids, normal_ids, tex_ids, active_material)) - - -class MeshData(): - """The loaded / parsed contents of a 3D Wavefront OBJ file. - - This is the intermediary step between the file on the disk, and a renderable - 3D object. It supports the subset of the OBJ file that was used in the - Vector and Cube assets, and does not attempt to exhaustively support every - possible setting. - - :param resource_manager: The resource manager to load this mesh from. - :param asset_key: The key of the OBJ file to load from the resource manager. - """ - - def __init__(self, resource_manager: ResourceManager, asset_key: str): - - # All worldspace vertex positions in this mesh (coordinates stored as list of 3 floats). - self._vertices: List[List[float]] = [] - # All directional vertex normals in this mesh (coordinates stored as list of 3 floats). - self._normals: List[List[float]] = [] - # All texture coordinates in this mesh (coordinates stored as list of 2 floats). - self._tex_coords: List[List[float]] = [] - - # All supported mesh groups, indexed by group name. - self._groups: Dict[str, MeshGroup] = {} - - # A container that MTL material attribute dicts can be fetched from by string key. - self._material_library: MaterialLibrary = None - - # Resource manager that can be used to load internally referenced assets - self._resource_manager = resource_manager - - self._logger = util.get_class_logger(__name__, self) - - self.load_from_obj_asset(asset_key) - - @property - def vertices(self) -> List[List[float]]: - """All worldspace vertex positions in this mesh.""" - return self._vertices - - @property - def normals(self) -> List[List[float]]: - """All directional vertex normals in this mesh.""" - return self._normals - - @property - def tex_coords(self) -> List[List[float]]: - """All texture coordinates in this mesh.""" - return self._tex_coords - - @property - def groups(self) -> Dict[str, MeshGroup]: - """All supported mesh groups, indexed by group name.""" - return self._groups - - @property - def material_library(self) -> MaterialLibrary: - """A container that MTL material attribute dicts can be fetched from by string key.""" - return self._material_library - - def load_from_obj_asset(self, asset_key: str): # pylint: disable=too-many-branches - """Loads all mesh data from a specified resource. - - :param asset_key: Key for the desired OBJ asset in the resource_manager. - """ - active_group_name: str = None - active_material: str = None - - file_data = self._resource_manager.load('assets', asset_key) - - for data_entry in file_data: - - line = data_entry.decode("utf-8") # Convert bytes to string - if line.startswith('#'): - # ignore comments in the file - continue - - values = line.split() - if not values: - # ignore empty lines - continue - - if values[0] == 'v': - # vertex position - v = list(map(float, values[1:4])) - self._vertices.append(v) - elif values[0] == 'vn': - # vertex normal - v = list(map(float, values[1:4])) - self._normals.append(v) - elif values[0] == 'vt': - # texture coordinate - v = list(map(float, values[1:3])) - self._tex_coords.append(v) - elif values[0] in ('usemtl', 'usemat'): - # material - active_material = values[1] - elif values[0] == 'mtllib': - # material library (a filename) - self._material_library = MaterialLibrary(self._resource_manager, values[1]) - elif values[0] == 'f': - if active_group_name not in self._groups: - self._groups[active_group_name] = MeshGroup(active_group_name) - - group = self._groups[active_group_name] - group.add_face_by_obj_data(values[1:], active_material) - - elif values[0] == 'o': - # object name - ignore - continue - elif values[0] == 'g': - # group name (for a sub-mesh) - active_group_name = values[1] - elif values[0] == 's': - # smooth shading (1..20, and 'off') - ignore - continue - else: - self._logger.warning("LoadedObjFile Ignoring unhandled type '%s' in line %s", - values[0], values) - - -class PrecomputedView(): - """A collection of static 3D object which are pre-computed on the graphics card, so they can - be quickly drawn when needed.""" - - def __init__(self): - self._display_lists = {} - - @staticmethod - def _apply_material(material: dict): - """Utility function to apply a specific MTL material to the current - OpenGL rendering state. - - :param material: A dictionary of MTL attributes defining a material. - """ - def _as_rgba(color): - if len(color) >= 4: - return color - # RGB - add alpha defaulted to 1 - return color + [1.0] - - if 'texture_Kd' in material: - # use diffuse texture map - glBindTexture(GL_TEXTURE_2D, material['texture_Kd']) - else: - # No texture map - glBindTexture(GL_TEXTURE_2D, 0) - - # Diffuse light - mtl_kd_rgba = _as_rgba(material['Kd']) - glColor(mtl_kd_rgba) - - # Ambient light - if 'Ka' in material: - mtl_ka_rgba = _as_rgba(material['Ka']) - glMaterialfv(GL_FRONT, GL_AMBIENT, mtl_ka_rgba) - glMaterialfv(GL_FRONT, GL_DIFFUSE, mtl_kd_rgba) - else: - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mtl_kd_rgba) - - # Specular light - if 'Ks' in material: - mtl_ks_rgba = _as_rgba(material['Ks']) - glMaterialfv(GL_FRONT, GL_SPECULAR, mtl_ks_rgba) - if 'Ns' in material: - specular_exponent = material['Ns'] - glMaterialfv(GL_FRONT, GL_SHININESS, specular_exponent) - - def build_from_mesh_data(self, mesh_data: MeshData): - """Uses a loaded mesh to create 3d geometry to store in the view. - - All groups in the mesh will pre-computed and stored by name. - - :param mesh_data: the source data that 3d geometry will be pre-computed from. - """ - material_library = mesh_data.material_library - - for key in mesh_data.groups: - new_gl_list = glGenLists(1) # pylint: disable=assignment-from-no-return - glNewList(new_gl_list, GL_COMPILE) - - group = mesh_data.groups[key] - - glEnable(GL_TEXTURE_2D) - glFrontFace(GL_CCW) - - for face in group.faces: - self._apply_material(material_library.get_material_by_name(face.material)) - - # Polygon (N verts) with optional normals and tex coords - glBegin(GL_POLYGON) - for i in range(face.vertex_count): - normal_index = face.normal_ids[i] - if normal_index > 0: - glNormal3fv(mesh_data.normals[normal_index - 1]) - tex_coord_index = face.tex_ids[i] - if tex_coord_index > 0: - glTexCoord2fv(mesh_data.tex_coords[tex_coord_index - 1]) - glVertex3fv(mesh_data.vertices[face.position_ids[i] - 1]) - glEnd() - - glDisable(GL_TEXTURE_2D) - glEndList() - - self._display_lists[key] = new_gl_list - - def build_from_render_function(self, name: str, f: callable, *args): - """Uses an externally provided function to create 3d geometry to store in the view. - - :param name: the key this pre-computed geometry can be draw from. - :param f: the function used to create the 3d geometry. - :param args: any parameters the supplied function is expecting. - """ - new_gl_list = glGenLists(1) # pylint: disable=assignment-from-no-return - glNewList(new_gl_list, GL_COMPILE) - f(*args) - glEndList() - - self._display_lists[name] = new_gl_list - - def display_by_key(self, key: str): - """Renders a specific piece of geometry from the view collection. - - :param key: The pre-computed object to render. - """ - try: - glCallList(self._display_lists[key]) - except KeyError: - raise KeyError('No display list with key {0} present on PrerenderedView'.format(key)) - - def display_all(self): - """Renders all pre-computed geometry in the view collection.""" - for display_list in self._display_lists.values(): - glCallList(display_list) - - -class Camera(): - """Class containing the state of a 3d camera, used to transform all object in a scene with - relation to a particular point of view. - - This class is meant to be mutated at run time. - - :param look_at: The initial target of the camera. - :param up: The initial up vector of the camera. - :param distance: The initial distance between the camera and it's target. - :param pitch: The camera's current rotation about its X axis - :param yaw: The camera's current rotation about its up axis - """ - - def __init__(self, look_at: util.Vector3, up: util.Vector3, distance: float, pitch: float, yaw: float): - # Camera position and orientation defined by a look-at positions - # and a pitch/and yaw to rotate around that along with a distance - self._look_at = look_at - self._pitch = pitch - self._yaw = yaw - self._distance = distance - self._pos = util.Vector3(0, 0, 0) - self._up = up - self._update_pos() - - @property - def look_at(self) -> util.Vector3: - """The target position of the camera""" - return self._look_at - - @look_at.setter - def look_at(self, look_at): - self._look_at = look_at - - def move(self, forward_amount: float = 0.0, right_amount: float = 0.0, up_amount: float = 0.0): - """Offsets the camera's position incrementally. - - :param forward_amount: distance to move along the camera's current forward heading. - :param right_amount: distance to move along a right angle to the camera's current forward heading. - :param up_amount: distance to move along the camera's up vector. - """ - self._look_at += self._up * up_amount - - # Move forward/back and left/right - pitch = self._pitch - yaw = self._yaw - camera_offset = util.Vector3(math.cos(yaw), math.sin(yaw), math.sin(pitch)) - - heading = math.atan2(camera_offset.y, camera_offset.x) - half_pi = math.pi * 0.5 - - self._look_at += util.Vector3( - right_amount * math.cos(heading + half_pi), - right_amount * math.sin(heading + half_pi), - 0.0) - - self._look_at += util.Vector3( - forward_amount * math.cos(heading), - forward_amount * math.sin(heading), - 0.0) - - def zoom(self, amount: float): - """Moves the camera closer or further from it's target point. - - :param amount: distance to zoom in or out, automatically minimized at 0.1 - """ - self._distance = max(0.1, self._distance + amount) - - def turn(self, yaw_delta: float, pitch_delta: float): - """Incrementally turns the camera. - - :param yaw_delta: Amount to rotate around the camera's up axis. - :param pitch_delta: Amount to rotate around the camera's X axis. This is automatically capped between +/- pi/2 - """ - # Adjust the Camera pitch and yaw - self._pitch = (self._pitch - pitch_delta) - self._yaw = (self._yaw + yaw_delta) % (2.0 * math.pi) - - # Clamp pitch to slightyly less than pi/2 to avoid lock/errors at full up/down - max_rotation = math.pi * 0.49 - self._pitch = max(-max_rotation, min(max_rotation, self._pitch)) - - def _update_pos(self): - """Calculate camera position based on look-at, distance and angles.""" - cos_pitch = math.cos(self._pitch) - sin_pitch = math.sin(self._pitch) - cos_yaw = math.cos(self._yaw) - sin_yaw = math.sin(self._yaw) - cam_distance = self._distance - cam_look_at = self._look_at - - self._pos = util.Vector3( - cam_look_at.x + (cam_distance * cos_pitch * cos_yaw), - cam_look_at.y + (cam_distance * cos_pitch * sin_yaw), - cam_look_at.z + (cam_distance * sin_pitch)) - - def apply(self): - """Applies the transform this camera represents to the active OpenGL context.""" - self._update_pos() - - gluLookAt(*self._pos.x_y_z, - *self._look_at.x_y_z, - *self._up.x_y_z) - - -class Projector(): # pylint: disable=too-few-public-methods - """Configuration for how 3d objects are projected onto the 2d window. - - :param fov: (Field of View) The viewing angle in degrees between the center of the window, and the top/bottom. - :param near_clip_plane: The minimum distance from the camera at which geometry can be rendered. - :param far_clip_plane: The maximum distance from the camera at which geometry can be rendered. - """ - - def __init__(self, fov: float, near_clip_plane: float, far_clip_plane: float): - self._fov = fov - self._near_clip_plane = near_clip_plane - self._far_clip_plane = far_clip_plane - - def apply(self, window): - """Applies the transform this projection represents to the active OpenGL context.""" - - # Set up the projection matrix - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - fov = self._fov - aspect_ratio = window.width / window.height - near_clip_plane = self._near_clip_plane - far_clip_plane = self._far_clip_plane - gluPerspective(fov, aspect_ratio, near_clip_plane, far_clip_plane) - - # Switch to model matrix for rendering everything - glMatrixMode(GL_MODELVIEW) - glLoadIdentity() - - -class Light(): # pylint: disable=too-few-public-methods - """Configuration for a light in the OpenGL scene that effects the shading of 3d geometry. - - :param ambient_color: Color applied to all geometry in the scene regardless of their relation to the light. - :param diffuse_color: Color applied to geometry in the scene which is facing the light. - :param specular_color: Color applied to geometry that is facing signficantly enough toward the light (depending on it's material's shininess). - :param position: The location of the light (or direction vector in the case of direction lights). - :param is_directional: Flag that sets the light to shine globally from a specified direction rather than an origin point (i.e. Sun light). - """ - - def __init__(self, ambient_color: List[float], diffuse_color: List[float], specular_color: List[float], position: util.Vector3, is_directional: bool = False): - self._ambient_color = ambient_color - self._diffuse_color = diffuse_color - self._specular_color = specular_color - # w coordinate of '1' indicates a positional light in OpenGL, while '0' would indicate a directional light - self._position_coords = [position.x, position.y, position.z, 0 if is_directional else 1] - - def apply(self, index: int): - """Applies this light to the active OpenGL context. - - :param index: the internal OpenGL light index to apply this class's data to. - """ - opengl_index = GL_LIGHT0 + index - glLightfv(opengl_index, GL_AMBIENT, self._ambient_color) - glLightfv(opengl_index, GL_DIFFUSE, self._diffuse_color) - glLightfv(opengl_index, GL_SPECULAR, self._specular_color) - glLightfv(opengl_index, GL_POSITION, self._position_coords) - glEnable(opengl_index) - - -class OpenGLWindow(): - """A Window displaying an OpenGL viewport. - - :param x: The initial x coordinate of the window in pixels. - :param y: The initial y coordinate of the window in pixels. - :param width: The initial height of the window in pixels. - :param height: The initial height of the window in pixels. - :param window_name: The name / title for the window. - """ - - def __init__(self, x: int, y: int, width: int, height: int, window_name: str): - self._pos = (x, y) - #: int: The width of the window - self._width = width - #: int: The height of the window - self._height = height - self._gl_window = None - self._window_name = window_name - - @property - def width(self): - """The horizontal width of the window""" - return self._width - - @property - def height(self): - """The vertical height of the window:""" - return self._height - - def initialize(self, display_function: callable): - """Initialze the OpenGL display parts of the Window. - - Warning: - Must be called on the same thread as OpenGL (usually the main thread), - """ - - glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) - - glutInitWindowSize(self.width, self.height) - glutInitWindowPosition(*self._pos) - self._gl_window = glutCreateWindow(self._window_name) - - glClearColor(0, 0, 0, 0) - glEnable(GL_DEPTH_TEST) - glShadeModel(GL_SMOOTH) - - glutIdleFunc(self._idle) - glutVisibilityFunc(self._visible) - glutReshapeFunc(self._reshape) - - glutDisplayFunc(display_function) - - def prepare_for_rendering(self, projector: Projector, camera: Camera, lights: List[Light]): - """Selects the window, clears buffers, and sets up the scene transform and lighting state. - - :param projector: The projector configuration to use for this rendering pass. - :param camera: The camera object to use for this rendering pass. - :param lights: The light list to use for this rendering pass. - """ - glutSetWindow(self._gl_window) - - # Clear the screen and the depth buffer - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - - # Apply our projection so the window is ready to render in perspective - projector.apply(self) - - # Add scene lights - light_count = len(lights) - for i in range(light_count): - lights[i].apply(i) - - # Set scale from mm to cm - glScalef(0.1, 0.1, 0.1) - - # Orient the camera - camera.apply() - - @staticmethod - def display_rendered_content(): - """Swaps buffers once rendering is finished. - """ - glutSwapBuffers() - - def _idle(self): # pylint: disable=no-self-use - """Called from OpenGL when idle.""" - glutPostRedisplay() - - def _visible(self, vis): - """Called from OpenGL when visibility changes (windows are either visible - or completely invisible/hidden).""" - if vis == GLUT_VISIBLE: - glutIdleFunc(self._idle) - else: - glutIdleFunc(None) - - def _reshape(self, width: int, height: int): - """Called from OpenGL whenever this window is resized. - - :param width: the new width of the window in pixels. - :param height: the new height of the window in pixels. - """ - self._width = width - self._height = height - glViewport(0, 0, width, height) diff --git a/anki_vector/opengl/opengl_vector.py b/anki_vector/opengl/opengl_vector.py deleted file mode 100644 index 138fab4..0000000 --- a/anki_vector/opengl/opengl_vector.py +++ /dev/null @@ -1,603 +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 -# -# https://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. - -"""This module provides Vector-specific 3D support classes for OpenGL, used by opengl_viewer.py. - -Warning: - This package requires Python to have the PyOpenGL package installed, along - with an implementation of GLUT (OpenGL Utility Toolkit). - - 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). - - On Linux: ``sudo apt-get install freeglut3`` - - On Windows: Go to http://freeglut.sourceforge.net/ to get a ``freeglut.dll`` - file. It's included in any of the `Windows binaries` downloads. Place the DLL - next to your Python script, or install it somewhere in your PATH to allow any - script to use it." -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['CubeRenderFrame', 'FaceRenderFrame', 'LightCubeView', 'RobotRenderFrame', 'RobotView', - 'UnitCubeView', 'VectorViewManifest', 'WorldRenderFrame'] - -import math -import time -from typing import List - -from anki_vector.faces import Face -from anki_vector.objects import CustomObject, FixedCustomObject, LightCube, ObservableObject -from anki_vector import nav_map, util -from . import opengl - -try: - from OpenGL.GL import (GL_AMBIENT, GL_BLEND, GL_COMPILE, GL_DIFFUSE, GL_FILL, GL_FRONT, GL_FRONT_AND_BACK, GL_LIGHTING, GL_LINE, GL_LINE_STRIP, - GL_ONE_MINUS_SRC_ALPHA, GL_POLYGON, GL_SHININESS, GL_SPECULAR, GL_SRC_ALPHA, GL_TRIANGLE_STRIP, - glBegin, glBlendFunc, glCallList, glColor, glColor3f, glColor4f, glDisable, glEnable, glEnd, glEndList, glGenLists, - glMaterialfv, glMultMatrixf, glNewList, glNormal3fv, glPolygonMode, glPopMatrix, glPushMatrix, glRotatef, glScalef, - glTranslatef, glVertex3f, glVertex3fv) - -except ImportError as import_exc: - opengl.raise_opengl_or_pillow_import_error(import_exc) - - -#: The object file used to render the robot. -VECTOR_MODEL_FILE = "vector.obj" - -#: The object file used to render the cube. -CUBE_MODEL_FILE = "cube.obj" - -# The following offsets are used in displaying the Vector 3d model. -# These values are tuned to reflect the vector.obj file, and do not -# necessarily reflect the actual measurements of the physical robot. - -#: The length of Vector's lift arm -LIFT_ARM_LENGTH_MM = 66.0 - -#: The height above ground of Vector's lift arm's pivot -LIFT_PIVOT_HEIGHT_MM = 45.0 - -#: Angle of the lift in the object's initial default pose. -LIFT_ANGLE_IN_DEFAULT_POSE = -11.36 - -#: Pivot offset for where the fork rotates around itself -FORK_PIVOT_X = 3.0 -FORK_PIVOT_Z = 3.4 - -#: Offset for the axel that the upper arm rotates around. -UPPER_ARM_PIVOT_X = -3.73 -UPPER_ARM_PIVOT_Z = 4.47 - -#: Offset for the axel that the lower arm rotates around. -LOWER_ARM_PIVOT_X = -3.74 -LOWER_ARM_PIVOT_Z = 3.27 - -#: Offset for the pivot that the head rotates around. -HEAD_PIVOT_X = -1.1 -HEAD_PIVOT_Z = 4.75 - - -_resource_package = __name__ # All resources are in subdirectories from this file's location - - -class UnitCubeView(opengl.PrecomputedView): - """A view containing a cube of unit size at the origin.""" - - def __init__(self): - - self._display_list_name = 'cube' - - super(UnitCubeView, self).__init__() - self.build_from_render_function(self._display_list_name, self._render_cube) - - @staticmethod - def _render_cube(): - """Pre renders a unit-size cube, with normals, centered at the origin. - """ - # build each of the 6 faces - for face_index in range(6): - # calculate normal and vertices for this face - vertex_normal = [0.0, 0.0, 0.0] - vertex_pos_options1 = [-1.0, 1.0, 1.0, -1.0] - vertex_pos_options2 = [1.0, 1.0, -1.0, -1.0] - face_index_even = ((face_index % 2) == 0) - # odd and even faces point in opposite directions - normal_dir = 1.0 if face_index_even else -1.0 - if face_index < 2: - # -X and +X faces (vert positions differ in Y,Z) - vertex_normal[0] = normal_dir - v1i = 1 - v2i = 2 - elif face_index < 4: - # -Y and +Y faces (vert positions differ in X,Z) - vertex_normal[1] = normal_dir - v1i = 0 - v2i = 2 - else: - # -Z and +Z faces (vert positions differ in X,Y) - vertex_normal[2] = normal_dir - v1i = 0 - v2i = 1 - - vertex_pos = list(vertex_normal) - - # Polygon (N verts) with optional normals and tex coords - glBegin(GL_POLYGON) - for vert_index in range(4): - vertex_pos[v1i] = vertex_pos_options1[vert_index] - vertex_pos[v2i] = vertex_pos_options2[vert_index] - glNormal3fv(vertex_normal) - glVertex3fv(vertex_pos) - glEnd() - - def display(self, color: List[float], draw_solid: bool): - """Displays the cube with a specific color. - - :param color: Color to display the cube. - :param draw_solid: Whether to draw solid polygons (False to draw wireframe). - """ - glColor(color) - - if draw_solid: - ambient_color = [color[0] * 0.1, color[1] * 0.1, color[2] * 0.1, 1.0] - else: - ambient_color = color - glMaterialfv(GL_FRONT, GL_AMBIENT, ambient_color) - glMaterialfv(GL_FRONT, GL_DIFFUSE, color) - glMaterialfv(GL_FRONT, GL_SPECULAR, color) - - glMaterialfv(GL_FRONT, GL_SHININESS, 10.0) - - if draw_solid: - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) - else: - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) - - self.display_by_key(self._display_list_name) - - -class LightCubeView(opengl.PrecomputedView): - """A view containing the Vector Light Cube 3D Model. - - :param mesh_data: Source Mesh Data for the light cube. - """ - - def __init__(self, mesh_data: opengl.MeshData): - - super(LightCubeView, self).__init__() - self.build_from_mesh_data(mesh_data) - - def display(self, pose: util.Pose): - """Displays the precomputed view at a specific pose in 3d space. - - :param pose: Where to display the cube. - """ - glPushMatrix() - - # TODO if cube_pose.is_accurate is False, render half-translucent? - # (This would require using a shader, or having duplicate objects) - - cube_matrix = pose.to_matrix() - glMultMatrixf(cube_matrix.in_row_order) - - # Cube is drawn slightly larger than the 10mm to 1 cm scale, as the model looks small otherwise - cube_scale_amt = 10.7 - glScalef(cube_scale_amt, cube_scale_amt, cube_scale_amt) - - self.display_all() - glPopMatrix() - - -class RobotView(opengl.PrecomputedView): - """A view containing the Vector robot 3D Model. - - :param mesh_data: Source Mesh Data for the robot. - """ - - def __init__(self, mesh_data: opengl.MeshData): - - super(RobotView, self).__init__() - self.build_from_mesh_data(mesh_data) - - def _display_vector_body(self): - """Displays the robot's body to the current OpenGL context - """ - - # Render the static body meshes - first the main body: - self.display_by_key("body_geo") - # Render the left treads and wheels - self.display_by_key("trackBase_L_geo") - self.display_by_key("wheel_BL_geo") - self.display_by_key("wheel_FL_geo") - self.display_by_key("tracks_L_geo") - # Render the right treads and wheels - self.display_by_key("trackBase_R_geo") - self.display_by_key("wheel_BR_geo") - self.display_by_key("wheel_FR_geo") - self.display_by_key("tracks_R_geo") - - def _display_vector_lift(self, lift_angle: float): - """Displays the robot's lift to the current OpenGL context - - :param lift_angle: the angle of the lift in radians - """ - - # Render the fork at the front (but not the arms) - glPushMatrix() - # The fork rotates first around upper arm (to get it to the correct position). - glTranslatef(UPPER_ARM_PIVOT_X, 0.0, UPPER_ARM_PIVOT_Z) - glRotatef(lift_angle, 0, 1, 0) - glTranslatef(-UPPER_ARM_PIVOT_X, 0.0, -UPPER_ARM_PIVOT_Z) - # The fork then rotates back around itself as it always hangs vertically. - glTranslatef(FORK_PIVOT_X, 0.0, FORK_PIVOT_Z) - glRotatef(-lift_angle, 0, 1, 0) - glTranslatef(-FORK_PIVOT_X, 0.0, -FORK_PIVOT_Z) - # Render - self.display_by_key("fork_geo") - glPopMatrix() - - # Render the upper arms: - glPushMatrix() - # Rotate the upper arms around the upper arm joint - glTranslatef(UPPER_ARM_PIVOT_X, 0.0, UPPER_ARM_PIVOT_Z) - glRotatef(lift_angle, 0, 1, 0) - glTranslatef(-UPPER_ARM_PIVOT_X, 0.0, -UPPER_ARM_PIVOT_Z) - # Render - self.display_by_key("uprArm_L_geo") - self.display_by_key("uprArm_geo") - glPopMatrix() - - # Render the lower arms: - glPushMatrix() - # Rotate the lower arms around the lower arm joint - glTranslatef(LOWER_ARM_PIVOT_X, 0.0, LOWER_ARM_PIVOT_Z) - glRotatef(lift_angle, 0, 1, 0) - glTranslatef(-LOWER_ARM_PIVOT_X, 0.0, -LOWER_ARM_PIVOT_Z) - # Render - self.display_by_key("lwrArm_L_geo") - self.display_by_key("lwrArm_R_geo") - glPopMatrix() - - def _display_vector_head(self, head_angle: float): - """Displays the robot's head to the current OpenGL context - - :param head_angle: the angle of the lift in radians - """ - - glPushMatrix() - # Rotate the head around the pivot - glTranslatef(HEAD_PIVOT_X, 0.0, HEAD_PIVOT_Z) - glRotatef(-head_angle, 0, 1, 0) - glTranslatef(-HEAD_PIVOT_X, 0.0, -HEAD_PIVOT_Z) - # Render all of the head meshes - self.display_by_key("head_geo") - # Screen - self.display_by_key("backScreen_mat") - self.display_by_key("screenEdge_geo") - self.display_by_key("overscan_1_geo") - # Eyes - self.display_by_key("eye_L_geo") - self.display_by_key("eye_R_geo") - # Eyelids - self.display_by_key("eyeLid_R_top_geo") - self.display_by_key("eyeLid_L_top_geo") - self.display_by_key("eyeLid_L_btm_geo") - self.display_by_key("eyeLid_R_btm_geo") - # Face cover (drawn last as it's translucent): - self.display_by_key("front_Screen_geo") - glPopMatrix() - - def display(self, pose: util.Pose, head_angle: util.Angle, lift_position: util.Distance): - """Displays the precomputed view at a specific pose in 3d space. - - :param pose: Where to display the robot. - """ - if not self._display_lists: - return - - robot_matrix = pose.to_matrix() - head_angle_degrees = head_angle.degrees - - # Get the angle of Vector's lift for rendering - we subtract the angle - # of the lift in the default pose in the object, and apply the inverse - # rotation - sin_angle = (lift_position.distance_mm - LIFT_PIVOT_HEIGHT_MM) / LIFT_ARM_LENGTH_MM - angle_radians = math.asin(sin_angle) - - lift_angle = -(angle_radians - LIFT_ANGLE_IN_DEFAULT_POSE) - - glPushMatrix() - glEnable(GL_LIGHTING) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - glEnable(GL_BLEND) - - glMultMatrixf(robot_matrix.in_row_order) - - robot_scale_amt = 10.0 # cm to mm - glScalef(robot_scale_amt, robot_scale_amt, robot_scale_amt) - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) - - self._display_vector_body() - self._display_vector_lift(lift_angle) - self._display_vector_head(head_angle_degrees) - - glDisable(GL_LIGHTING) - glPopMatrix() - - -class NavMapView(opengl.PrecomputedView): - """A view containing a cube of unit size at the origin.""" - - def __init__(self): - self.logger = util.get_class_logger(__name__, self) - super(NavMapView, self).__init__() - - def build_from_nav_map(self, new_nav_map: nav_map.NavMapGrid): - """Reconstructs the display list for the NavMapView based on a :class:`anki_vector.nav_map.NavMapGrid` object. - - :param new_nav_map: nav map source data to be referenced for the new display list. - """ - cen = new_nav_map.center - half_size = new_nav_map.size * 0.5 - - self._display_lists['_navmap'] = glGenLists(1) # pylint: disable=assignment-from-no-return - glNewList(self._display_lists['_navmap'], GL_COMPILE) - - glPushMatrix() - - color_light_gray = (0.65, 0.65, 0.65) - glColor3f(*color_light_gray) - glBegin(GL_LINE_STRIP) - glVertex3f(cen.x + half_size, cen.y + half_size, cen.z) # TL - glVertex3f(cen.x + half_size, cen.y - half_size, cen.z) # TR - glVertex3f(cen.x - half_size, cen.y - half_size, cen.z) # BR - glVertex3f(cen.x - half_size, cen.y + half_size, cen.z) # BL - glVertex3f(cen.x + half_size, cen.y + half_size, - cen.z) # TL (close loop) - glEnd() - - def color_for_content(content): - nct = nav_map.NavNodeContentTypes - colors = {nct.Unknown.value: (0.3, 0.3, 0.3), # dark gray - nct.ClearOfObstacle.value: (0.0, 1.0, 0.0), # green - nct.ClearOfCliff.value: (0.0, 0.5, 0.0), # dark green - nct.ObstacleCube.value: (1.0, 0.0, 0.0), # red - nct.ObstacleProximity.value: (1.0, 0.5, 0.0), # orange - nct.ObstacleProximityExplored.value: (0.5, 1.0, 0.0), # yellow-green - nct.ObstacleUnrecognized.value: (0.5, 0.0, 0.0), # dark red - nct.Cliff.value: (0.0, 0.0, 0.0), # black - nct.InterestingEdge.value: (1.0, 1.0, 0.0), # yellow - nct.NonInterestingEdge.value: (0.5, 0.5, 0.0), # dark-yellow - } - - col = colors.get(content) - if col is None: - col = (1.0, 1.0, 1.0) # white - return col - - fill_z = cen.z - 0.4 - - def _recursive_draw(grid_node: nav_map.NavMapGridNode): - if grid_node.children is not None: - for child in grid_node.children: - _recursive_draw(child) - else: - # leaf node - render as a quad - map_alpha = 0.5 - cen = grid_node.center - half_size = grid_node.size * 0.5 - - # Draw outline - glColor4f(*color_light_gray, 1.0) # fully opaque - glBegin(GL_LINE_STRIP) - glVertex3f(cen.x + half_size, cen.y + half_size, cen.z) - glVertex3f(cen.x + half_size, cen.y - half_size, cen.z) - glVertex3f(cen.x - half_size, cen.y - half_size, cen.z) - glVertex3f(cen.x - half_size, cen.y + half_size, cen.z) - glVertex3f(cen.x + half_size, cen.y + half_size, cen.z) - glEnd() - - # Draw filled contents - glColor4f(*color_for_content(grid_node.content), map_alpha) - glBegin(GL_TRIANGLE_STRIP) - glVertex3f(cen.x + half_size, cen.y - half_size, fill_z) - glVertex3f(cen.x + half_size, cen.y + half_size, fill_z) - glVertex3f(cen.x - half_size, cen.y - half_size, fill_z) - glVertex3f(cen.x - half_size, cen.y + half_size, fill_z) - glEnd() - - _recursive_draw(new_nav_map.root_node) - - glPopMatrix() - glEndList() - - def display(self): - """Displays the precomputed nav map view. - This function will do nothing if no display list has yet been built. - """ - if '_navmap' in self._display_lists: - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - glEnable(GL_BLEND) - glPushMatrix() - glCallList(self._display_lists['_navmap']) - glPopMatrix() - - -class VectorViewManifest(): - """A collection of Vector-specific source data containing views to display. - """ - - def __init__(self): - self._light_cube_view: LightCubeView = None - self._unit_cube_view: UnitCubeView = None - self._robot_view: RobotView = None - self._nav_map_view: NavMapView = None - - @property - def light_cube_view(self) -> LightCubeView: - """A precomputed view of Vector's light cube.""" - return self._light_cube_view - - @property - def unit_cube_view(self) -> UnitCubeView: - """A precomputed view of a unit cube. - - This is used for representing detected faces. - """ - return self._unit_cube_view - - @property - def robot_view(self) -> RobotView: - """A precomputed view of the robot.""" - return self._robot_view - - @property - def nav_map_view(self) -> NavMapView: - """A precomputable view of the navigation map. This will be updated - as new content comes in. - """ - return self._nav_map_view - - def load_assets(self): - """Loads all assets needed for the view manifest, and precomputes them - into cached views. - """ - resource_context = opengl.ResourceManager(_resource_package) - - # Load 3D objects - robot_mesh_data = opengl.MeshData(resource_context, VECTOR_MODEL_FILE) - self._robot_view = RobotView(robot_mesh_data) - - # Load the cube - cube_mesh_data = opengl.MeshData(resource_context, CUBE_MODEL_FILE) - self._light_cube_view = LightCubeView(cube_mesh_data) - - self._unit_cube_view = UnitCubeView() - - self._nav_map_view = NavMapView() - - -class ObservableObjectRenderFrame(): # pylint: disable=too-few-public-methods - """Minimal copy of an object's state for 1 frame of rendering. - - :param obj: the cube object to be rendered. - """ - - def __init__(self, obj: ObservableObject): - self.pose = obj.pose - self.is_visible = obj.is_visible - self.last_observed_time = obj.last_observed_time - - @property - def time_since_last_seen(self) -> float: - # Equivalent of ObservableObject's method - """time since this obj was last seen (math.inf if never)""" - if self.last_observed_time is None: - return math.inf - return time.time() - self.last_observed_time - - -class CubeRenderFrame(ObservableObjectRenderFrame): # pylint: disable=too-few-public-methods - """Minimal copy of a Cube's state for 1 frame of rendering. - - :param cube: the cube object to be rendered. - """ - - def __init__(self, cube: LightCube): # pylint: disable=useless-super-delegation - super().__init__(cube) - - -class FaceRenderFrame(ObservableObjectRenderFrame): # pylint: disable=too-few-public-methods - """Minimal copy of a Face's state for 1 frame of rendering. - - :param face: The face object to be rendered. - """ - - def __init__(self, face: Face): # pylint: disable=useless-super-delegation - super().__init__(face) - - -class CustomObjectRenderFrame(ObservableObjectRenderFrame): # pylint: disable=too-few-public-methods - """Minimal copy of a CustomObject's state for 1 frame of rendering. - - :param custom_object: The custom object to be rendered. Either :class:`anki_vector.objects.CustomObject` or :class:`anki_vector.objects.FixedCustomObject`. - :param is_fixed: Whether the custom object is permanently defined rather than an observable archetype. - """ - - def __init__(self, custom_object, is_fixed: bool): - if is_fixed: - # Not an observable, so init directly - self.pose = custom_object.pose - self.is_visible = None - self.last_observed_time = None - else: - super().__init__(custom_object) - - self.is_fixed = is_fixed - self.x_size_mm = custom_object.archetype.x_size_mm - self.y_size_mm = custom_object.archetype.y_size_mm - self.z_size_mm = custom_object.archetype.z_size_mm - - -class RobotRenderFrame(): # pylint: disable=too-few-public-methods - """Minimal copy of a Robot's state for 1 frame of rendering. - - :param robot: the robot object to be rendered. - """ - - def __init__(self, robot): - self.pose = robot.pose - if robot.head_angle_rad is None: - self.head_angle = util.radians(0.0) - else: - self.head_angle = util.radians(robot.head_angle_rad) - if robot.lift_height_mm is None: - self.lift_position = util.distance_mm(0.0) - else: - self.lift_position = util.distance_mm(robot.lift_height_mm) - - -class WorldRenderFrame(): # pylint: disable=too-few-public-methods - """Minimal copy of the World's state for 1 frame of rendering. - - :param robot: the robot object to be rendered, which also has handles to the other objects - defined in it's world class. - """ - - def __init__(self, robot): - - self.robot_frame = RobotRenderFrame(robot) - - self.cube_frames: List[CubeRenderFrame] = [] - if robot.world.connected_light_cube is not None: - self.cube_frames.append(CubeRenderFrame(robot.world.connected_light_cube)) - - self.face_frames: List[FaceRenderFrame] = [] - for face in robot.world.visible_faces: - # Ignore faces that have a newer version (with updated id) - # or if they haven't been seen in a while. - if not face.has_updated_face_id and (face.time_since_last_seen < 60): - self.face_frames.append(FaceRenderFrame(face)) - - self.custom_object_frames = [] - for obj in robot.world.all_objects: - is_custom = isinstance(obj, CustomObject) - is_fixed = isinstance(obj, FixedCustomObject) - if is_custom or is_fixed: - self.custom_object_frames.append(CustomObjectRenderFrame(obj, is_fixed)) diff --git a/anki_vector/opengl/opengl_viewer.py b/anki_vector/opengl/opengl_viewer.py deleted file mode 100644 index 43a38ec..0000000 --- a/anki_vector/opengl/opengl_viewer.py +++ /dev/null @@ -1,679 +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 -# -# https://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. - -"""This module provides a 3D visualizer for Vector's world state and a 2D camera window. - -It uses PyOpenGL, a Python OpenGL 3D graphics library which is available on most -platforms. It also depends on the Pillow library for image processing. - -Example: - .. testcode:: - - import time - - import anki_vector - - with anki_vector.Robot(show_viewer=True, - show_3d_viewer=True, - enable_camera_feed=True, - enable_face_detection=True, - enable_custom_object_detection=True, - enable_nav_map_feed=True) as robot: - time.sleep(10) - -Warning: - This package requires Python to have the PyOpenGL package installed, along - with an implementation of GLUT (OpenGL Utility Toolkit). - - 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). - - On Linux: ``sudo apt-get install freeglut3`` - - On Windows: Go to http://freeglut.sourceforge.net/ to get a ``freeglut.dll`` - file. It's included in any of the `Windows binaries` downloads. Place the DLL - next to your Python script, or install it somewhere in your PATH to allow any - script to use it." -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['OpenGLViewer'] - -import math -import multiprocessing as mp -import sys -from typing import List - -from anki_vector import nav_map, util -from . import opengl, opengl_vector - - -try: - from OpenGL.GL import (GL_FILL, - GL_FRONT_AND_BACK, - GL_LIGHTING, GL_NORMALIZE, - GL_TEXTURE_2D, - glBindTexture, glColor3f, glDisable, glEnable, - glMultMatrixf, glPolygonMode, glPopMatrix, glPushMatrix, - glScalef, glWindowPos2f) - from OpenGL.GLUT import (ctypes, - GLUT_ACTIVE_ALT, GLUT_ACTIVE_CTRL, GLUT_ACTIVE_SHIFT, GLUT_BITMAP_9_BY_15, - GLUT_DOWN, GLUT_LEFT_BUTTON, GLUT_RIGHT_BUTTON, GLUT_VISIBLE, - glutBitmapCharacter, glutCheckLoop, glutGetModifiers, glutIdleFunc, - glutKeyboardFunc, glutKeyboardUpFunc, glutMainLoop, glutMouseFunc, glutMotionFunc, glutPassiveMotionFunc, - glutPostRedisplay, glutSpecialFunc, glutSpecialUpFunc, glutVisibilityFunc) - from OpenGL.error import NullFunctionError - -except ImportError as import_exc: - opengl.raise_opengl_or_pillow_import_error(import_exc) - - -# Constants - - -class _RobotControlIntents(): # pylint: disable=too-few-public-methods - """Input intents for controlling the robot. - - These are sent from the OpenGL process, and consumed by the main process for - issuing movement commands on Vector (to provide a remote-control interface). - """ - - def __init__(self, left_wheel_speed=0.0, right_wheel_speed=0.0, - lift_speed=0.0, head_speed=0.0): - self.left_wheel_speed = left_wheel_speed - self.right_wheel_speed = right_wheel_speed - self.lift_speed = lift_speed - self.head_speed = head_speed - - -def _draw_text(font, input_str, x, y, line_height=16, r=1.0, g=1.0, b=1.0): - """Render text based on window position. The origin is in the bottom-left.""" - glColor3f(r, g, b) - glWindowPos2f(x, y) - input_list = input_str.split('\n') - y = y + (line_height * (len(input_list) - 1)) - for line in input_list: - glWindowPos2f(x, y) - y -= line_height - for ch in line: - glutBitmapCharacter(font, ctypes.c_int(ord(ch))) - - -def _glut_install_instructions(): - if sys.platform.startswith('linux'): - return "Install freeglut: `sudo apt-get install freeglut3`" - if sys.platform.startswith('darwin'): - return "GLUT should already be installed by default on macOS!" - if sys.platform in ('win32', 'cygwin'): - return "Install freeglut: You can download it from http://freeglut.sourceforge.net/ \n"\ - "You just need the `freeglut.dll` file, from any of the 'Windows binaries' downloads. "\ - "Place the DLL next to your Python script, or install it somewhere in your PATH "\ - "to allow any script to use it." - return "(Instructions unknown for platform %s)" % sys.platform - - -class _OpenGLViewController(): - """Controller that registers for keyboard and mouse input through GLUT, and uses them to update - the camera and listen for a shutdown cue. - - :param shutdown_delegate: Function to call when we want to exit the host OpenGLViewer. - :param camera: The camera object for the controller to mutate. - :param input_intent_queue: Sends key commands from the 3D viewer process to the main process. - :type input_intent_queue: multiprocessing.Queue - :param viewer: A reference to the owning OpenGLViewer. - :type viewer: OpenGLViewer - """ - - def __init__(self, shutdown_delegate: callable, camera: opengl.Camera, input_intent_queue: mp.Queue, viewer): - - self._logger = util.get_class_logger(__name__, self) - self._input_intent_queue = input_intent_queue - self._last_robot_control_intents = _RobotControlIntents() - self._is_keyboard_control_enabled = False - - # Keyboard - self._is_key_pressed = {} - self._is_alt_down = False - self._is_ctrl_down = False - self._is_shift_down = False - - # Mouse - self._is_mouse_down = {} - self._mouse_pos = None # type: util.Vector2 - - self._shutdown_delegate = shutdown_delegate - - self._last_robot_position = None - - self._camera = camera - - self._opengl_viewer = viewer - - #### Public Properties #### - - @property - def last_robot_position(self): - return self._last_robot_position - - @last_robot_position.setter - def last_robot_position(self, last_robot_position): - self._last_robot_position = last_robot_position - - #### Public Methods #### - - def initialize(self): - """Sets up the OpenGL window and binds input callbacks to it - """ - - glutKeyboardFunc(self._on_key_down) - glutSpecialFunc(self._on_special_key_down) - - # [Keyboard/Special]Up methods aren't supported on some old GLUT implementations - has_keyboard_up = False - has_special_up = False - try: - if bool(glutKeyboardUpFunc): - glutKeyboardUpFunc(self._on_key_up) - has_keyboard_up = True - if bool(glutSpecialUpFunc): - glutSpecialUpFunc(self._on_special_key_up) - has_special_up = True - except NullFunctionError: - # Methods aren't available on this GLUT version - pass - - if not has_keyboard_up or not has_special_up: - # Warn on old GLUT implementations that don't implement much of the interface. - self._logger.warning("Warning: Old GLUT implementation detected - keyboard remote control of Vector disabled." - "We recommend installing freeglut. %s", _glut_install_instructions()) - self._is_keyboard_control_enabled = False - else: - self._is_keyboard_control_enabled = True - - try: - GLUT_BITMAP_9_BY_15 - except NameError: - self._logger.warning("Warning: GLUT font not detected. Help message will be unavailable.") - - glutMouseFunc(self._on_mouse_button) - glutMotionFunc(self._on_mouse_move) - glutPassiveMotionFunc(self._on_mouse_move) - - glutIdleFunc(self._idle) - glutVisibilityFunc(self._visible) - - #### Private Methods #### - - def _update_modifier_keys(self): - """Updates alt, ctrl, and shift states. - """ - modifiers = glutGetModifiers() - self._is_alt_down = (modifiers & GLUT_ACTIVE_ALT != 0) - self._is_ctrl_down = (modifiers & GLUT_ACTIVE_CTRL != 0) - self._is_shift_down = (modifiers & GLUT_ACTIVE_SHIFT != 0) - - def _key_byte_to_lower(self, key): # pylint: disable=no-self-use - """Convert bytes-object (representing keyboard character) to lowercase equivalent. - """ - if b'A' <= key <= b'Z': - lowercase_key = ord(key) - ord(b'A') + ord(b'a') - lowercase_key = bytes([lowercase_key]) - return lowercase_key - return key - - def _on_key_up(self, key, x, y): # pylint: disable=unused-argument - """Called by GLUT when a standard keyboard key is released. - - :param key: which key was released. - :param x: the x coordinate of the mouse cursor. - :param y: the y coordinate of the mouse cursor. - """ - key = self._key_byte_to_lower(key) - self._update_modifier_keys() - self._is_key_pressed[key] = False - - def _on_key_down(self, key, x, y): # pylint: disable=unused-argument - """Called by GLUT when a standard keyboard key is pressed. - - :param key: which key was released. - :param x: the x coordinate of the mouse cursor. - :param y: the y coordinate of the mouse cursor. - """ - key = self._key_byte_to_lower(key) - self._update_modifier_keys() - self._is_key_pressed[key] = True - - if ord(key) == 9: # Tab - # Set Look-At point to current robot position - if self._last_robot_position is not None: - self._camera.look_at = self._last_robot_position - elif ord(key) == 27: # Escape key - self._shutdown_delegate() - elif ord(key) == 72 or ord(key) == 104: # H key - self._opengl_viewer.show_controls = not self._opengl_viewer.show_controls - - def _on_special_key_up(self, key, x, y): # pylint: disable=unused-argument - """Called by GLUT when a special key is released. - - :param key: which key was released. - :param x: the x coordinate of the mouse cursor. - :param y: the y coordinate of the mouse cursor. - """ - self._update_modifier_keys() - - def _on_special_key_down(self, key, x, y): # pylint: disable=unused-argument - """Called by GLUT when a special key is pressed. - - :param key: which key was pressed. - :param x: the x coordinate of the mouse cursor. - :param y: the y coordinate of the mouse cursor. - """ - self._update_modifier_keys() - - def _on_mouse_button(self, button, state, x, y): - """Called by GLUT when a mouse button is pressed. - - :param button: which button was pressed. - :param state: the current state of the button. - :param x: the x coordinate of the mouse cursor. - :param y: the y coordinate of the mouse cursor. - """ - # Don't update modifier keys- reading modifier keys is unreliable - # from _on_mouse_button (for LMB down/up), only SHIFT key seems to read there - # self._update_modifier_keys() - is_down = (state == GLUT_DOWN) - self._is_mouse_down[button] = is_down - self._mouse_pos = util.Vector2(x, y) - - def _on_mouse_move(self, x, y): - """Handles mouse movement. - - :param x: the x coordinate of the mouse cursor. - :param y: the y coordinate of the mouse cursor. - """ - - # is_active is True if this is not passive (i.e. a mouse button was down) - last_mouse_pos = self._mouse_pos - self._mouse_pos = util.Vector2(x, y) - if last_mouse_pos is None: - # First mouse update - ignore (we need a delta of mouse positions) - return - - left_button = self._is_mouse_down.get(GLUT_LEFT_BUTTON, False) - # For laptop and other 1-button mouse users, treat 'x' key as a right mouse button too - right_button = (self._is_mouse_down.get(GLUT_RIGHT_BUTTON, False) or - self._is_key_pressed.get(b'x', False)) - - MOUSE_SPEED_SCALAR = 1.0 # general scalar for all mouse movement sensitivity - MOUSE_ROTATE_SCALAR = 0.025 # additional scalar for rotation sensitivity - mouse_delta = (self._mouse_pos - last_mouse_pos) * MOUSE_SPEED_SCALAR - - if left_button and right_button: - # Move up/down - self._camera.move(up_amount=-mouse_delta.y) - elif right_button: - # Move forward/back and left/right - self._camera.move(forward_amount=mouse_delta.y, right_amount=mouse_delta.x) - elif left_button: - if self._is_key_pressed.get(b'z', False): - # Zoom in/out - self._camera.zoom(mouse_delta.y) - else: - self._camera.turn(mouse_delta.x * MOUSE_ROTATE_SCALAR, mouse_delta.y * MOUSE_ROTATE_SCALAR) - - def _update_intents_for_robot(self): - # Update driving intents based on current input, and pass to SDK thread - # so that it can pass the input onto the robot. - def get_intent_direction(key1, key2): - # Helper for keyboard inputs that have 1 positive and 1 negative input - pos_key = self._is_key_pressed.get(key1, False) - neg_key = self._is_key_pressed.get(key2, False) - return pos_key - neg_key - - drive_dir = get_intent_direction(b'w', b's') - turn_dir = get_intent_direction(b'd', b'a') - lift_dir = get_intent_direction(b'r', b'f') - head_dir = get_intent_direction(b't', b'g') - if drive_dir < 0: - # It feels more natural to turn the opposite way when reversing - turn_dir = -turn_dir - - # Scale drive speeds with SHIFT (faster) and ALT (slower) - if self._is_shift_down: - speed_scalar = 2.0 - elif self._is_alt_down: - speed_scalar = 0.5 - else: - speed_scalar = 1.0 - - drive_speed = 75.0 * speed_scalar - turn_speed = 100.0 * speed_scalar - - left_wheel_speed = (drive_dir * drive_speed) + (turn_speed * turn_dir) - right_wheel_speed = (drive_dir * drive_speed) - (turn_speed * turn_dir) - lift_speed = 4.0 * lift_dir * speed_scalar - head_speed = head_dir * speed_scalar - - control_intents = _RobotControlIntents(left_wheel_speed, right_wheel_speed, - lift_speed, head_speed) - self._input_intent_queue.put(control_intents, True) - - def _idle(self): - if self._is_keyboard_control_enabled: - self._update_intents_for_robot() - glutPostRedisplay() - - def _visible(self, vis): - # Called from OpenGL when visibility changes (windows are either visible - # or completely invisible/hidden) - if vis == GLUT_VISIBLE: - glutIdleFunc(self._idle) - else: - glutIdleFunc(None) - - -#: A default window resolution provided for OpenGL Vector programs -#: 800x600 is large enough to see detail, while fitting on the smaller -#: end of modern monitors. -default_resolution = [800, 600] - -#: A default projector configurate provided for OpenGL Vector programs -#: A Field of View of 45 degrees is common for 3d applications, -#: and a viewable distance range of 1.0 to 1000.0 will provide a -#: visible space comparable with most physical Vector environments. -default_projector = opengl.Projector( - fov=45.0, - near_clip_plane=1.0, - far_clip_plane=1000.0) - -#: A default camera object provided for OpenGL Vector programs. -#: Starts close to and looking at the charger. -default_camera = opengl.Camera( - look_at=util.Vector3(100.0, -25.0, 0.0), - up=util.Vector3(0.0, 0.0, 1.0), - distance=500.0, - pitch=math.radians(40), - yaw=math.radians(270)) - -#: A default light group provided for OpenGL Vector programs. -#: Contains one light near the origin. -default_lights = [opengl.Light( - ambient_color=[1.0, 1.0, 1.0, 1.0], - diffuse_color=[1.0, 1.0, 1.0, 1.0], - specular_color=[1.0, 1.0, 1.0, 1.0], - position=util.Vector3(0, 32, 20))] - -# Global viewer instance. Stored to make sure multiple viewers are not -# instantiated simultaneously. -opengl_viewer = None # type: OpenGLViewer - - -class OpenGLViewer(): - """OpenGL-based 3D Viewer. - - Handles rendering of a 3D world view including navigation map. - - :param close_event: Used to notify each process when done rendering. - :type close_event: multiprocessing.Event - :param input_intent_queue: Sends key commands from the 3D viewer process to the main process. - :type input_intent_queue: multiprocessing.Queue - :param nav_map_queue: Updates the 3D viewer process with the latest navigation map. - :type nav_map_queue: multiprocessing.Queue - :param world_frame_queue: Provides the 3D viewer with details about the world. - :type world_frame_queue: multiprocessing.Queue - :param extra_render_function_queue: Functions to be executed in the 3D viewer process. - :type extra_render_function_queue: multiprocessing.Queue - :param user_data_queue: A queue that may be used outside the SDK to pass information to the viewer process. - May be used by ``extra_render_function_queue`` functions. - :type user_data_queue: multiprocessing.Queue - :param resolution: Specifies whether to draw controls on the view. - :param projector: Specifies whether to draw controls on the view. - :param camera: Specifies whether to draw controls on the view. - :param lights: Specifies whether to draw controls on the view. - :param show_viewer_controls: Specifies whether to draw controls on the view. - """ - - def __init__(self, - close_event: mp.Event, - input_intent_queue: mp.Queue, - nav_map_queue: mp.Queue, - world_frame_queue: mp.Queue, - extra_render_function_queue: mp.Queue, - user_data_queue: mp.Queue, - resolution: List[int] = None, - projector: opengl.Projector = None, - camera: opengl.Camera = None, - lights: List[opengl.Light] = None, - show_viewer_controls: bool = True): - if resolution is None: - resolution = default_resolution - if projector is None: - projector = default_projector - if camera is None: - camera = default_camera - if lights is None: - lights = default_lights - - self._close_event = close_event - self._input_intent_queue = input_intent_queue - self._nav_map_queue = nav_map_queue - self._world_frame_queue = world_frame_queue - self._extra_render_function_queue = extra_render_function_queue - self._user_data_queue = user_data_queue - - self._logger = util.get_class_logger(__name__, self) - self._extra_render_calls = [] - - self._internal_function_finished = False - - # Controls - self.show_controls = show_viewer_controls - self._instructions = '\n'.join(['W, S: Move forward, backward', - 'A, D: Turn left, right', - 'R, F: Lift up, down', - 'T, G: Head up, down', - '', - 'LMB: Rotate camera', - 'RMB: Move camera', - 'LMB + RMB: Move camera up/down', - 'LMB + Z: Zoom camera', - 'X: same as RMB', - 'TAB: center view on robot', - '', - 'H: Toggle help']) - - self._vector_view_manifest = opengl_vector.VectorViewManifest() - self._main_window = opengl.OpenGLWindow(0, 0, resolution[0], resolution[1], b"Vector 3D Visualizer") - - # Create a 3d projector configuration class. - self._projector = projector - self._camera = camera - self._lights = lights - - self._view_controller = _OpenGLViewController(self.close, self._camera, self._input_intent_queue, self) - - self._latest_world_frame: opengl_vector.WorldRenderFrame = None - - def _render_world_frame(self, world_frame: opengl_vector.WorldRenderFrame): - """Render the world to the current OpenGL context - - :param world_frame: frame to render - """ - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) - glEnable(GL_NORMALIZE) # to re-scale scaled normals - - light_cube_view = self._vector_view_manifest.light_cube_view - unit_cube_view = self._vector_view_manifest.unit_cube_view - robot_view = self._vector_view_manifest.robot_view - nav_map_view = self._vector_view_manifest.nav_map_view - - robot_frame = world_frame.robot_frame - robot_pose = robot_frame.pose - - try: - glDisable(GL_LIGHTING) - nav_map_view.display() - - glEnable(GL_LIGHTING) - # Render the cube - for obj in world_frame.cube_frames: - cube_pose = obj.pose - if cube_pose is not None and cube_pose.is_comparable(robot_pose): - light_cube_view.display(cube_pose) - - # Render the custom objects - for obj in world_frame.custom_object_frames: - obj_pose = obj.pose - if obj_pose is not None and obj_pose.is_comparable(robot_pose): - glPushMatrix() - obj_matrix = obj_pose.to_matrix() - glMultMatrixf(obj_matrix.in_row_order) - - glScalef(obj.x_size_mm * 0.5, - obj.y_size_mm * 0.5, - obj.z_size_mm * 0.5) - - # Only draw solid object for observable custom objects - - if obj.is_fixed: - # fixed objects are drawn as transparent outlined boxes to make - # it clearer that they have no effect on vision. - FIXED_OBJECT_COLOR = [1.0, 0.7, 0.0, 1.0] - unit_cube_view.display(FIXED_OBJECT_COLOR, False) - else: - CUSTOM_OBJECT_COLOR = [1.0, 0.3, 0.3, 1.0] - unit_cube_view.display(CUSTOM_OBJECT_COLOR, True) - - glPopMatrix() - - glBindTexture(GL_TEXTURE_2D, 0) - - for face in world_frame.face_frames: - face_pose = face.pose - if face_pose is not None and face_pose.is_comparable(robot_pose): - glPushMatrix() - face_matrix = face_pose.to_matrix() - glMultMatrixf(face_matrix.in_row_order) - - # Approximate size of a head - glScalef(100, 25, 100) - - FACE_OBJECT_COLOR = [0.5, 0.5, 0.5, 1.0] - draw_solid = face.time_since_last_seen < 30 - unit_cube_view.display(FACE_OBJECT_COLOR, draw_solid) - - glPopMatrix() - except BaseException as e: - self._logger.error('rendering error: {0}'.format(e)) - - glDisable(GL_LIGHTING) - - # Draw the Vector robot to the screen - robot_view.display(robot_frame.pose, robot_frame.head_angle, robot_frame.lift_position) - - if self.show_controls: - self._draw_controls() - - def _draw_controls(self): - try: - GLUT_BITMAP_9_BY_15 - except NameError: - pass - else: - _draw_text(GLUT_BITMAP_9_BY_15, self._instructions, x=10, y=10) - - def _render_3d_view(self, window: opengl.OpenGLWindow): - """Renders 3d objects to an openGL window - - :param window: OpenGL window to render to - """ - window.prepare_for_rendering(self._projector, self._camera, self._lights) - - try: - extra_render_call = self._extra_render_function_queue.get(False) - self._extra_render_calls.append(extra_render_call) - except mp.queues.Empty: - pass - - # Update the latest world frame if there is a new one available - try: - world_frame = self._world_frame_queue.get(False) # type: WorldRenderFrame - if world_frame is not None: - self._view_controller.last_robot_position = world_frame.robot_frame.pose.position - self._latest_world_frame = world_frame - except mp.queues.Empty: - world_frame = self._latest_world_frame - - try: - new_nav_map = self._nav_map_queue.get(False) - if new_nav_map is not None: - new_nav_map = nav_map.NavMapGrid(new_nav_map, self._logger) - self._vector_view_manifest.nav_map_view.build_from_nav_map(new_nav_map) - except mp.queues.Empty: - # no new nav map - queue is empty - pass - - if world_frame is not None: - self._render_world_frame(world_frame) - - for render_call in self._extra_render_calls: - # Protecting the external calls with pushMatrix so internal transform - # state changes will not alter other calls - glPushMatrix() - try: - render_call.invoke(self._user_data_queue) - finally: - glPopMatrix() - - window.display_rendered_content() - - def _on_window_update(self): - """Top level display call. - """ - try: - self._render_3d_view(self._main_window) - - except KeyboardInterrupt: - self._logger.info("_display caught KeyboardInterrupt - exitting") - self._close_event.set() - - def run(self): - """Turns control of the current thread over to the OpenGL viewer - """ - self._main_window.initialize(self._on_window_update) - self._view_controller.initialize() - - self._vector_view_manifest.load_assets() - - # use a non-blocking update loop if possible to make exit conditions - # easier (not supported on all GLUT versions). - if bool(glutCheckLoop): - while not self._close_event.is_set(): - glutCheckLoop() - else: - # This blocks until quit - glutMainLoop() - - if not self._close_event.is_set(): - # Pass the keyboard interrupt on to SDK so that it can close cleanly - raise KeyboardInterrupt - - def close(self): - """Called from the SDK when the program is complete and it's time to exit.""" - if not self._close_event.is_set(): - self._close_event.set() diff --git a/anki_vector/photos.py b/anki_vector/photos.py deleted file mode 100644 index 0686be6..0000000 --- a/anki_vector/photos.py +++ /dev/null @@ -1,145 +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 -# -# https://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. - -""" -Photo related classes, functions, events and values. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ["PhotographComponent"] - -import concurrent -from typing import List - -from . import connection, util -from .messaging import protocol - - -class PhotographComponent(util.Component): - """Access the photos on Vector. - - .. testcode:: - - import anki_vector - import io - from PIL import Image - - with anki_vector.Robot() as robot: - for photo_info in robot.photos.photo_info: - print(f"Opening photo {photo_info.photo_id}") - photo = robot.photos.get_photo(photo_info.photo_id) - image = Image.open(io.BytesIO(photo.image)) - image.show() - - :param anki_vector.Robot robot: A reference to an instance of the Robot class. Used to make rpc calls. - """ - - def __init__(self, robot): - super().__init__(robot) - self._photo_info: List[protocol.PhotoInfo] = [] - - @property - def photo_info(self) -> List[protocol.PhotoInfo]: - """The information about what photos are stored on Vector. - - If the photo info hasn't been loaded yet, accessing this property will request it from the robot. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - for photo_info in robot.photos.photo_info: - print(f"photo_info.photo_id: {photo_info.photo_id}") # the id to use to grab a photo from the robot - print(f"photo_info.timestamp_utc: {photo_info.timestamp_utc}") # utc timestamp of when the photo was taken (according to the robot) - """ - if not self._photo_info: - self.logger.debug("Photo list was empty. Lazy-loading photo list now.") - result = self.load_photo_info() - if isinstance(result, concurrent.futures.Future): - result.result() - return self._photo_info - - @connection.on_connection_thread() - async def load_photo_info(self) -> protocol.PhotosInfoResponse: - """Request the photo information from the robot. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - photo_info = robot.photos.load_photo_info() - print(f"photo_info: {photo_info}") - - :return: UTC timestamp of the photo and additional data. - """ - req = protocol.PhotosInfoRequest() - result = await self.grpc_interface.PhotosInfo(req) - self._photo_info = result.photo_infos - return result - - @connection.on_connection_thread(log_messaging=False) - async def get_photo(self, photo_id: int) -> protocol.PhotoResponse: - """Download a full-resolution photo from the robot's storage. - - .. testcode:: - - import anki_vector - import io - from PIL import Image - - with anki_vector.Robot() as robot: - for photo_info in robot.photos.photo_info: - print(f"Opening photo {photo_info.photo_id}") - photo = robot.photos.get_photo(photo_info.photo_id) - image = Image.open(io.BytesIO(photo.image)) - image.show() - - :param photo_id: The id of the photo to download. It's recommended to get this - value from the photo_info list first. - - :return: A response containing all of the photo bytes which may be rendered using - another library (like :mod:`PIL`) - """ - req = protocol.PhotoRequest(photo_id=photo_id) - return await self.grpc_interface.Photo(req) - - @connection.on_connection_thread(log_messaging=False) - async def get_thumbnail(self, photo_id: int) -> protocol.ThumbnailResponse: - """Download a thumbnail of a given photo from the robot's storage. - - You may use this function to pull all of the images off the robot in a smaller format, and - then determine which one to download as full resolution. - - .. testcode:: - - import anki_vector - from PIL import Image - import io - - with anki_vector.Robot() as robot: - for photo_info in robot.photos.photo_info: - photo = robot.photos.get_thumbnail(photo_info.photo_id) - image = Image.open(io.BytesIO(photo.image)) - image.show() - - :param photo_id: The id of the thumbnail to download. It's recommended to get this - value from the photo_info list first. - - :return: A response containing all of the thumbnail bytes which may be rendered using - another library (like :mod:`PIL`) - """ - req = protocol.ThumbnailRequest(photo_id=photo_id) - return await self.grpc_interface.Thumbnail(req) diff --git a/anki_vector/proximity.py b/anki_vector/proximity.py deleted file mode 100755 index 9f93937..0000000 --- a/anki_vector/proximity.py +++ /dev/null @@ -1,229 +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 -# -# https://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. - -""" -Support for Vector's distance sensor. - -Vector's time-of-flight distance sensor has a usable range of about 30 mm to 1200 mm -(max useful range closer to 300mm for Vector) with a field of view of 25 degrees. - -The distance sensor can be used to detect objects in front of the robot. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ["ProximityComponent", "ProximitySensorData"] - -from . import util -from .events import Events -from .messaging import protocol - - -class ProximitySensorData: - """A distance sample from the time-of-flight sensor with metadata describing reliability of the measurement - - The proximity sensor is located near the bottom of Vector between the two front wheels, facing forward. The - reported distance describes how far in front of this sensor the robot feels an obstacle is. The sensor estimates - based on time-of-flight information within a field of view which the engine resolves to a certain quality value. - - Four additional flags are supplied by the engine to indicate whether this proximity data is considered valid - for the robot's internal pathfinding. Respecting these is optional, but will help python code respect the - behavior of the robot's innate object avoidance. - """ - - def __init__(self, proto_data: protocol.ProxData): - self._distance = util.Distance(distance_mm=proto_data.distance_mm) - self._signal_quality = proto_data.signal_quality - self._is_in_valid_range = proto_data.is_in_valid_range - self._is_valid_signal_quality = proto_data.is_valid_signal_quality - self._is_lift_in_fov = proto_data.is_lift_in_fov - self._is_too_pitched = proto_data.is_too_pitched - - @property - @util.block_while_none() - def distance(self) -> float: - """The distance between the sensor and a detected object - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - distance = robot.proximity.last_sensor_reading.distance - """ - return self._distance - - @property - @util.block_while_none() - def signal_quality(self) -> float: - """The quality of the detected object. - - The proximity sensor detects obstacles within a given field of view, - this value represents the likelihood of the reported distance being - a solid surface. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - signal_quality = robot.proximity.last_sensor_reading.signal_quality - """ - return self._signal_quality - - @property - @util.block_while_none() - def is_in_valid_range(self) -> bool: - """Whether or not the engine considers the detected signal is close enough - to be considered useful. Past a certain threshold, distance readings - become unreliable. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - is_in_valid_range = robot.proximity.last_sensor_reading.is_in_valid_range - """ - return self._is_in_valid_range - - @property - @util.block_while_none() - def is_valid_signal_quality(self) -> bool: - """Whether the engine considers the detected signal to be reliable enough - to be considered an object in proximity. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - is_valid_signal_quality = robot.proximity.last_sensor_reading.is_valid_signal_quality - """ - return self._is_valid_signal_quality - - @property - @util.block_while_none() - def is_lift_in_fov(self) -> bool: - """Whether Vector's lift is blocking the time-of-flight sensor. While - the lift will send clear proximity signals, it's not useful for object - detection. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - is_lift_in_fov = robot.proximity.last_sensor_reading.is_lift_in_fov - """ - return self._is_lift_in_fov - - @property - @util.block_while_none() - def is_too_pitched(self) -> bool: - """Whether the engine considers the robot to be tilted too much up or down - for the time-of-flight data to usefully describe obstacles in the driving - plane. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - is_too_pitched = robot.proximity.last_sensor_reading.is_too_pitched - """ - return self._is_too_pitched - - @property - @util.block_while_none() - def is_valid(self) -> bool: - """Comprehensive judgment of whether the reported distance is useful for - object proximity detection. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - is_valid = robot.proximity.last_sensor_reading.is_valid - """ - return self._is_in_valid_range and self._is_valid_signal_quality and not self._is_lift_in_fov and not self._is_too_pitched - - -class ProximityComponent(util.Component): - """Maintains the most recent proximity sensor data - - This will be updated with every broadcast RobotState, and can be queried at any time. Two sensor readings are made available: - - the most recent data from the robot - - the most recent data which was considered valid by the engine for usage - - An example of how to extract sensor data: - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - proximity_data = robot.proximity.last_sensor_reading - if proximity_data is not None: - print('Proximity distance: {0}, engine considers useful: {1}'.format(proximity_data.distance, proximity_data.is_valid)) - """ - - def __init__(self, robot): - super().__init__(robot) - self._last_valid_sensor_reading = None - self._last_sensor_reading = None - - # 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, - 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) - - @property - @util.block_while_none() - def last_sensor_reading(self) -> ProximitySensorData: - """:class:`anki_vector.proximity.ProximitySensorData`: The last reported sensor data. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - last_sensor_reading = robot.proximity.last_sensor_reading - """ - return self._last_sensor_reading - - @property - @util.block_while_none() - def last_valid_sensor_reading(self) -> ProximitySensorData: - """:class:`anki_vector.proximity.ProximitySensorData`: The last reported sensor data - which is considered useful for object detection. - - ..code-block :: - - import anki_vector - - with anki_vector.Robot() as robot: - last_valid_sensor_reading = robot.proximity.last_valid_sensor_reading - """ - return self._last_valid_sensor_reading - - def _on_robot_state(self, _, msg): - self._last_sensor_reading = ProximitySensorData(msg.prox_data) - if self._last_sensor_reading.is_valid: - self._last_valid_sensor_reading = self._last_sensor_reading diff --git a/anki_vector/robot.py b/anki_vector/robot.py deleted file mode 100755 index 12fbeb1..0000000 --- a/anki_vector/robot.py +++ /dev/null @@ -1,907 +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 -# -# https://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. - -""" -The main robot class for managing Vector. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['AsyncRobot', 'Robot'] - -import concurrent -import configparser -import functools -from pathlib import Path - -from . import (animation, audio, behavior, camera, - connection, events, exceptions, faces, - motors, nav_map, screen, photos, proximity, - status, touch, util, viewer, vision, world) -from .viewer import (ViewerComponent, Viewer3DComponent) -from .messaging import protocol - - -class Robot: - """The Robot object is responsible for managing the state and connections - to a Vector, and is typically the entry-point to running the sdk. - - The majority of the robot will not work until it is properly connected - to Vector. There are two ways to get connected: - - 1. Using :code:`with`: it works just like opening a file, and will close when - the :code:`with` block's indentation ends. - - - .. testcode:: - - import anki_vector - - # Create the robot connection - with anki_vector.Robot() as robot: - # Run your commands - robot.anim.play_animation("anim_turn_left_01") - - 2. Using :func:`connect` and :func:`disconnect` to explicitly open and close the connection: - it allows the robot's connection to continue in the context in which it started. - - .. testcode:: - - import anki_vector - - # Create a Robot object - robot = anki_vector.Robot() - # Connect to the Robot - robot.connect() - # Run your commands - robot.anim.play_animation("anim_turn_left_01") - # Disconnect from Vector - robot.disconnect() - - :param serial: Vector's serial number. The robot's serial number (ex. 00e20100) is located on the underside of Vector, - or accessible from Vector's debug screen. Used to identify which Vector configuration to load. - :param ip: Vector's IP address. (optional) - :param config: A custom :class:`dict` to override values in Vector's configuration. (optional) - Example: :code:`{"cert": "/path/to/file.cert", "name": "Vector-XXXX", "guid": ""}` - where :code:`cert` is the certificate to identify Vector, :code:`name` is the name on Vector's face - when his backpack is double-clicked on the charger, and :code:`guid` is the authorization token - that identifies the SDK user. Note: Never share your authentication credentials with anyone. - :param default_logging: Toggle default logging. - :param behavior_activation_timeout: The time to wait for control of the robot before failing. - :param cache_animation_list: Get the list of animations available at startup. - :param enable_face_detection: Turn on face detection. - :param enable_camera_feed: Turn camera feed on/off. - :param enable_audio_feed: Turn audio feed on/off. - :param enable_custom_object_detection: Turn custom object detection on/off. - :param enable_nav_map_feed: Turn navigation map feed on/off. - :param show_viewer: Render camera feed on/off. - :param show_3d_viewer: Render camera feed on/off. - :param requires_behavior_control: Request control of Vector's behavior system.""" - - def __init__(self, - serial: str = None, - ip: str = None, - config: dict = None, - default_logging: bool = True, - behavior_activation_timeout: int = 10, - cache_animation_list: bool = True, - enable_face_detection: bool = False, - enable_camera_feed: bool = False, - enable_audio_feed: bool = False, - enable_custom_object_detection: bool = False, - enable_nav_map_feed: bool = None, - show_viewer: bool = False, - show_3d_viewer: bool = False, - requires_behavior_control: bool = True): - if default_logging: - util.setup_basic_logging() - self.logger = util.get_class_logger(__name__, self) - self._force_async = False - config = config if config is not None else {} - config = {**self._read_configuration(serial), **config} - - self._name = config["name"] - self._ip = ip if ip is not None else config["ip"] - self._cert_file = config["cert"] - self._guid = config["guid"] - - self._port = "443" - if 'port' in config: - 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 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":""}') - - #: :class:`anki_vector.connection.Connection`: The active connection to the robot. - self._conn = connection.Connection(self._name, ':'.join([self._ip, self._port]), self._cert_file, self._guid, requires_behavior_control=requires_behavior_control) - self._events = events.EventHandler() - - # placeholders for components before they exist - self._anim: animation.AnimationComponent = None - self._audio: audio.AudioComponent = None - self._behavior: behavior.BehaviorComponent = None - self._camera: camera.CameraComponent = None - self._faces: faces.FaceComponent = None - self._motors: motors.MotorComponent = None - self._nav_map: nav_map.NavMapComponent = None - self._screen: screen.ScreenComponent = None - self._photos: photos.PhotographComponent = None - self._proximity: proximity.ProximityComponent = None - self._touch: touch.TouchComponent = None - self._viewer: viewer.ViewerComponent = None - self._viewer_3d: viewer.Viewer3DComponent = None - self._vision: vision.VisionComponent = None - self._world: world.World = None - - self.behavior_activation_timeout = behavior_activation_timeout - self.enable_face_detection = enable_face_detection - self.enable_custom_object_detection = enable_custom_object_detection - self.cache_animation_list = cache_animation_list - - # Robot state/sensor data - self._pose: util.Pose = None - self._pose_angle_rad: float = None - self._pose_pitch_rad: float = None - self._left_wheel_speed_mmps: float = None - self._right_wheel_speed_mmps: float = None - self._head_angle_rad: float = None - self._lift_height_mm: float = None - self._accel: util.Vector3 = None - self._gyro: util.Vector3 = None - self._carrying_object_id: float = None - self._head_tracking_object_id: float = None - self._localized_to_object_id: float = None - self._last_image_time_stamp: float = None - self._status: status.RobotStatus = status.RobotStatus() - self.pending = [] - - self._enable_camera_feed = enable_camera_feed - self._enable_audio_feed = enable_audio_feed - if enable_nav_map_feed is not None: - self._enable_nav_map_feed = enable_nav_map_feed - else: - self._enable_nav_map_feed = False - self._show_viewer = show_viewer - if show_viewer and not enable_camera_feed: - self.logger.warning("enable_camera_feed should be True for viewer to render correctly.") - self._enable_camera_feed = True - self._show_3d_viewer = show_3d_viewer - if show_3d_viewer and enable_nav_map_feed is None: - self.logger.warning("enable_nav_map_feed should be True for 3d viewer to render correctly.") - self._enable_nav_map_feed = True - - def _read_configuration(self, serial: str) -> dict: - """Open the default conf file, and read it into a :class:`configparser.ConfigParser` - - :param serial: Vector's serial number - """ - home = Path.home() / ".anki_vector" - conf_file = str(home / "sdk_config.ini") - parser = configparser.ConfigParser(strict=False) - parser.read(conf_file) - - sections = parser.sections() - if not sections: - 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)) - elif serial is None: - raise Exception('\n\nFound multiple robot serial numbers. Please provide the serial number of the Robot you want to control.\n' - 'Example: ./01_hello_world.py --serial {robot_serial_number}') - - serial = serial.lower() - config = {k.lower(): v for k, v in parser.items()} - try: - dict_entry = config[serial] - except KeyError: - raise Exception('\n\nCould not find matching robot info for given serial number: {}. Please check your serial number is correct.\n' - 'Example: ./01_hello_world.py --serial {{robot_serial_number}}'.format(serial)) - - return dict_entry - - @property - def force_async(self) -> bool: - """A reference to the Robot object instance.""" - return self._force_async - - @property - def conn(self) -> connection.Connection: - """A reference to the Connection instance.""" - return self._conn - - @property - def events(self) -> events.EventHandler: - """A reference to the EventHandler instance.""" - return self._events - - @property - def anim(self) -> animation.AnimationComponent: - """A reference to the AnimationComponent instance.""" - if self._anim is None: - raise exceptions.VectorNotReadyException("AnimationComponent is not yet initialized") - return self._anim - - @property - def audio(self) -> audio.AudioComponent: - """The audio instance used to control Vector's audio feed.""" - - print("\n\nNote: Audio stream is not yet supported and does not yet come from Vector's microphones.\n\n") - - if self._audio is None: - raise exceptions.VectorNotReadyException("AudioComponent is not yet initialized") - return self._audio - - @property - def behavior(self) -> behavior.BehaviorComponent: - """A reference to the BehaviorComponent instance.""" - return self._behavior - - @property - def camera(self) -> camera.CameraComponent: - """The camera instance used to control Vector's camera feed. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot(enable_camera_feed=True) as robot: - image = robot.camera.latest_image - image.show() - """ - if self._camera is None: - raise exceptions.VectorNotReadyException("CameraComponent is not yet initialized") - return self._camera - - @property - def faces(self) -> faces.FaceComponent: - """A reference to the FaceComponent instance.""" - if self._faces is None: - raise exceptions.VectorNotReadyException("FaceComponent is not yet initialized") - return self._faces - - @property - def motors(self) -> motors.MotorComponent: - """A reference to the MotorComponent instance.""" - if self._motors is None: - raise exceptions.VectorNotReadyException("MotorComponent is not yet initialized") - return self._motors - - @property - def nav_map(self) -> nav_map.NavMapComponent: - """A reference to the NavMapComponent instance.""" - if self._nav_map is None: - raise exceptions.VectorNotReadyException("NavMapComponent is not yet initialized") - return self._nav_map - - @property - def screen(self) -> screen.ScreenComponent: - """A reference to the ScreenComponent instance.""" - if self._screen is None: - raise exceptions.VectorNotReadyException("ScreenComponent is not yet initialized") - return self._screen - - @property - def photos(self) -> photos.PhotographComponent: - """A reference to the PhotographComponent instance.""" - if self._photos is None: - raise exceptions.VectorNotReadyException("PhotographyComponent is not yet initialized") - return self._photos - - @property - def proximity(self) -> proximity.ProximityComponent: - """Component containing state related to object proximity detection. - - ..code-block :: - - import anki_vector - with anki_vector.Robot() as robot: - proximity_data = robot.proximity.last_valid_sensor_reading - if proximity_data is not None: - print(proximity_data.distance) - """ - return self._proximity - - @property - def touch(self) -> touch.TouchComponent: - """Component containing state related to object touch detection. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - print('Robot is being touched: {0}'.format(robot.touch.last_sensor_reading.is_being_touched)) - """ - return self._touch - - @property - def viewer(self) -> ViewerComponent: - """The viewer instance used to render Vector's camera feed. - - .. testcode:: - - import time - - import anki_vector - - with anki_vector.Robot(show_viewer=True) as robot: - # Render video for 5 seconds - robot.viewer.show_video() - time.sleep(5) - - # Disable video render and camera feed for 5 seconds - robot.viewer.stop_video() - """ - if self._viewer is None: - raise exceptions.VectorNotReadyException("ViewerComponent is not yet initialized") - return self._viewer - - @property - def viewer_3d(self) -> Viewer3DComponent: - """The 3D viewer instance used to render Vector's navigation map. - - .. testcode:: - - import time - - import anki_vector - - with anki_vector.Robot(show_3d_viewer=True, enable_nav_map_feed=True) as robot: - # Render 3D view of navigation map for 5 seconds - time.sleep(5) - """ - if self._viewer_3d is None: - raise exceptions.VectorNotReadyException("Viewer3DComponent is not yet initialized") - return self._viewer_3d - - @property - def vision(self) -> vision.VisionComponent: - """Component containing functionality related to vision based object detection. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - robot.vision.enable_custom_object_detection() - """ - return self._vision - - @property - def world(self) -> world.World: - """A reference to the World instance, or None if the WorldComponent is not yet initialized.""" - if self._world is None: - raise exceptions.VectorNotReadyException("WorldComponent is not yet initialized") - return self._world - - @property - @util.block_while_none() - def pose(self) -> util.Pose: - """:class:`anki_vector.util.Pose`: The current pose (position and orientation) of Vector. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_robot_pose = robot.pose - """ - return self._pose - - @property - @util.block_while_none() - def pose_angle_rad(self) -> float: - """Vector's pose angle (heading in X-Y plane). - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_pose_angle_rad = robot.pose_angle_rad - """ - return self._pose_angle_rad - - @property - @util.block_while_none() - def pose_pitch_rad(self) -> float: - """Vector's pose pitch (angle up/down). - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_pose_pitch_rad = robot.pose_pitch_rad - """ - return self._pose_pitch_rad - - @property - @util.block_while_none() - def left_wheel_speed_mmps(self) -> float: - """Vector's left wheel speed in mm/sec - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_left_wheel_speed_mmps = robot.left_wheel_speed_mmps - """ - return self._left_wheel_speed_mmps - - @property - @util.block_while_none() - def right_wheel_speed_mmps(self) -> float: - """Vector's right wheel speed in mm/sec - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_right_wheel_speed_mmps = robot.right_wheel_speed_mmps - """ - return self._right_wheel_speed_mmps - - @property - @util.block_while_none() - def head_angle_rad(self) -> float: - """Vector's head angle (up/down). - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_head_angle_rad = robot.head_angle_rad - """ - return self._head_angle_rad - - @property - @util.block_while_none() - def lift_height_mm(self) -> float: - """Height of Vector's lift from the ground. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_lift_height_mm = robot.lift_height_mm - """ - return self._lift_height_mm - - @property - @util.block_while_none() - def accel(self) -> util.Vector3: - """:class:`anki_vector.util.Vector3`: The current accelerometer reading (x, y, z) - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_accel = robot.accel - """ - return self._accel - - @property - @util.block_while_none() - def gyro(self) -> util.Vector3: - """The current gyroscope reading (x, y, z) - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_gyro = robot.gyro - """ - return self._gyro - - @property - @util.block_while_none() - def carrying_object_id(self) -> int: - """The ID of the object currently being carried (-1 if none) - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_carrying_object_id = robot.carrying_object_id - """ - return self._carrying_object_id - - @property - @util.block_while_none() - def head_tracking_object_id(self) -> int: - """The ID of the object the head is tracking to (-1 if none) - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_head_tracking_object_id = robot.head_tracking_object_id - """ - return self._head_tracking_object_id - - @property - @util.block_while_none() - def localized_to_object_id(self) -> int: - """The ID of the object that the robot is localized to (-1 if none) - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_localized_to_object_id = robot.localized_to_object_id - """ - return self._localized_to_object_id - - # TODO Move to photos or somewhere else - @property - @util.block_while_none() - def last_image_time_stamp(self) -> int: - """The robot's timestamp for the last image seen. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - current_last_image_time_stamp = robot.last_image_time_stamp - """ - return self._last_image_time_stamp - - @property - 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: - if robot.status.is_being_held: - print("Vector is being held!") - else: - print("Vector is not being held.") - """ - return self._status - - @property - def enable_audio_feed(self) -> bool: - """The audio feed enabled/disabled - - :getter: Returns whether the audio feed is enabled - :setter: Enable/disable the audio feed - - .. code-block:: python - - import asyncio - import time - - import anki_vector - - with anki_vector.Robot(enable_audio_feed=True) as robot: - time.sleep(5) - robot.enable_audio_feed = False - time.sleep(5) - """ - # TODO When audio is ready, convert `.. code-block:: python` to `.. testcode::` - return self._enable_audio_feed - - @enable_audio_feed.setter - def enable_audio_feed(self, enable) -> None: - self._enable_audio_feed = enable - if self.enable_audio_feed: - self.audio.init_audio_feed() - - @property - def enable_camera_feed(self) -> bool: - """The camera feed enabled/disabled - - :getter: Returns whether the camera feed is enabled - :setter: Enable/disable the camera feed - - .. testcode:: - - import asyncio - import time - - import anki_vector - - with anki_vector.Robot(enable_camera_feed=True) as robot: - time.sleep(5) - robot.enable_camera_feed = False - time.sleep(5) - """ - return self._enable_camera_feed - - @enable_camera_feed.setter - def enable_camera_feed(self, enable) -> None: - self._enable_camera_feed = enable - if self.enable_camera_feed: - self.camera.init_camera_feed() - else: - self.camera.close_camera_feed() - - # Unpack streamed data to robot's internal properties - def _unpack_robot_state(self, _, msg): - self._pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z, - q0=msg.pose.q0, q1=msg.pose.q1, - q2=msg.pose.q2, q3=msg.pose.q3, - origin_id=msg.pose.origin_id) - self._pose_angle_rad = msg.pose_angle_rad - self._pose_pitch_rad = msg.pose_pitch_rad - self._left_wheel_speed_mmps = msg.left_wheel_speed_mmps - self._right_wheel_speed_mmps = msg.right_wheel_speed_mmps - self._head_angle_rad = msg.head_angle_rad - self._lift_height_mm = msg.lift_height_mm - self._accel = util.Vector3(msg.accel.x, msg.accel.y, msg.accel.z) - self._gyro = util.Vector3(msg.gyro.x, msg.gyro.y, msg.gyro.z) - self._carrying_object_id = msg.carrying_object_id - 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.set(msg.status) - - def connect(self, timeout: int = 10) -> None: - """Start the connection to Vector. - - .. testcode:: - - import anki_vector - - robot = anki_vector.Robot() - robot.connect() - robot.anim.play_animation("anim_turn_left_01") - robot.disconnect() - - :param timeout: The time to allow for a connection before a - :class:`anki_vector.exceptions.VectorTimeoutException` is raised. - """ - self.conn.connect(timeout=timeout) - self.events.start(self.conn) - - # Initialize components - self._anim = animation.AnimationComponent(self) - # self._audio = audio.AudioComponent(self) # TODO turn on - self._behavior = behavior.BehaviorComponent(self) - self._camera = camera.CameraComponent(self) - self._faces = faces.FaceComponent(self) - self._motors = motors.MotorComponent(self) - self._nav_map = nav_map.NavMapComponent(self) - self._screen = screen.ScreenComponent(self) - self._photos = photos.PhotographComponent(self) - self._proximity = proximity.ProximityComponent(self) - self._touch = touch.TouchComponent(self) - self._viewer = viewer.ViewerComponent(self) - self._viewer_3d = viewer.Viewer3DComponent(self) - self._vision = vision.VisionComponent(self) - self._world = world.World(self) - - if self.cache_animation_list: - # Load animations so they are ready to play when requested - anim_request = self._anim.load_animation_list() - if isinstance(anim_request, concurrent.futures.Future): - anim_request.result() - - # Start audio feed - if self.enable_audio_feed: - self.audio.init_audio_feed() - - # Start camera feed - if self.enable_camera_feed: - self.camera.init_camera_feed() - - # Start rendering camera feed - if self._show_viewer: - self.viewer.show_video() - - if self._show_3d_viewer: - self.viewer_3d.show() - - if self._enable_nav_map_feed: - self.nav_map.init_nav_map_feed() - - # Enable face detection, to allow Vector to add faces to its world view - if self.conn.requires_behavior_control: - face_detection = self.vision.enable_face_detection(detect_faces=self.enable_face_detection, estimate_expression=False) - if isinstance(face_detection, concurrent.futures.Future): - face_detection.result() - object_detection = self.vision.enable_custom_object_detection(detect_custom_objects=self.enable_custom_object_detection) - if isinstance(object_detection, concurrent.futures.Future): - 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, - on_connection_thread=True) - - # access the pose to prove it has gotten back from the event stream once. - while not self.pose: - pass - - def disconnect(self) -> None: - """Close the connection with Vector. - - .. testcode:: - - import anki_vector - robot = anki_vector.Robot() - robot.connect() - robot.anim.play_animation("anim_turn_left_01") - robot.disconnect() - """ - if self.conn.requires_behavior_control: - self.vision.close() - - # Stop rendering video - self.viewer.stop_video() - # Stop rendering 3d video - self.viewer_3d.close() - # Shutdown camera feed - self.camera.close_camera_feed() - self._enable_camera_feed = False - # Shutdown audio feed - if self._audio is not None: - self._audio.close_audio_feed() - # Shutdown nav map feed - self.nav_map.close_nav_map_feed() - # Close the world and cleanup its objects - self.world.close() - - self.proximity.close() - self.touch.close() - - self.events.close() - self.conn.close() - - def __enter__(self): - self.connect(self.behavior_activation_timeout) - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.disconnect() - - @connection.on_connection_thread(requires_control=False) - async def get_battery_state(self) -> protocol.BatteryStateResponse: - """Check the current state of the robot and cube batteries. - - Vector is considered fully-charged above 4.1 volts. At 3.6V, the robot is approaching low charge. - - Battery_level values are as follows: - | Low = 1: 3.6V or less. If on charger, 4V or less. - | Nominal = 2 - | Full = 3: This state can only be achieved when Vector is on the charger. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - battery_state = robot.get_battery_state() - if battery_state: - print("Robot battery voltage: {0}".format(battery_state.battery_volts)) - print("Robot battery Level: {0}".format(battery_state.battery_level)) - print("Robot battery is charging: {0}".format(battery_state.is_charging)) - print("Robot is on charger platform: {0}".format(battery_state.is_on_charger_platform)) - print("Robot's suggested charger time: {0}".format(battery_state.suggested_charger_sec)) - """ - get_battery_state_request = protocol.BatteryStateRequest() - return await self.conn.grpc_interface.BatteryState(get_battery_state_request) - - @connection.on_connection_thread(requires_control=False) - async def get_version_state(self) -> protocol.VersionStateResponse: - """Get the versioning information for Vector, including Vector's os_version and engine_build_id. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - version_state = robot.get_version_state() - """ - get_version_state_request = protocol.VersionStateRequest() - return await self.conn.grpc_interface.VersionState(get_version_state_request) - - @connection.on_connection_thread(requires_control=False) - async def get_network_state(self) -> protocol.NetworkStateResponse: - """Get the network information for Vector. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - network_state = robot.get_network_state() - """ - get_network_state_request = protocol.NetworkStateRequest() - return await self.conn.grpc_interface.NetworkState(get_network_state_request) - - @connection.on_connection_thread() - async def say_text(self, text: str, use_vector_voice: bool = True, duration_scalar: float = 1.0) -> protocol.SayTextResponse: - """Make Vector speak text. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - robot.say_text("Hello World") - - :param text: The words for Vector to say. - :param use_vector_voice: Whether to use Vector's robot voice - (otherwise, he uses a generic human male voice). - :param duration_scalar: Adjust the relative duration of the - generated text to speech audio. - - :return: object that provides the status and utterance state - """ - say_text_request = protocol.SayTextRequest(text=text, - use_vector_voice=use_vector_voice, - duration_scalar=duration_scalar) - return await self.conn.grpc_interface.SayText(say_text_request) - - -class AsyncRobot(Robot): - """The AsyncRobot object is just like the Robot object, but allows multiple commands - to be executed at the same time. To achieve this, all grpc function calls also - return a :class:`concurrent.futures.Future`. - - 1. Using :code:`with`: it works just like opening a file, and will close when - the :code:`with` block's indentation ends. - - .. testcode:: - - import anki_vector - # Create the robot connection - with anki_vector.AsyncRobot() as robot: - # Run your commands - robot.anim.play_animation("anim_turn_left_01").result() - - 2. Using :func:`connect` and :func:`disconnect` to explicitly open and close the connection: - it allows the robot's connection to continue in the context in which it started. - - .. testcode:: - - import anki_vector - # Create a Robot object - robot = anki_vector.AsyncRobot() - # Connect to Vector - robot.connect() - # Run your commands - robot.anim.play_animation("anim_turn_left_01").result() - # Disconnect from Vector - robot.disconnect() - - :param serial: Vector's serial number. Used to identify which Vector configuration to load. - :param ip: Vector's IP Address. (optional) - :param config: A custom :class:`dict` to override values in Vector's configuration. (optional) - Example: :code:`{"cert": "/path/to/file.cert", "name": "Vector-XXXX", "guid": ""}` - where :code:`cert` is the certificate to identify Vector, :code:`name` is the name on Vector's face - when his backpack is double-clicked on the charger, and :code:`guid` is the authorization token - that identifies the SDK user. Note: Never share your authentication credentials with anyone. - :param default_logging: Toggle default logging. - :param behavior_activation_timeout: The time to wait for control of the robot before failing. - :param enable_face_detection: Turn on face detection. - :param enable_camera_feed: Turn camera feed on/off. - :param enable_audio_feed: Turn audio feed on/off. - :param show_viewer: Render camera feed on/off. - :param requires_behavior_control: Request control of Vector's behavior system.""" - - @functools.wraps(Robot.__init__) - def __init__(self, *args, **kwargs): - super(AsyncRobot, self).__init__(*args, **kwargs) - self._force_async = True - - # TODO Should be private? Better method name? If not private, Add docstring and sample code - def add_pending(self, task): - self.pending += [task] - - # TODO Should be private? Better method name? If not private, Add docstring and sample code - def remove_pending(self, task): - self.pending = [x for x in self.pending if x is not task] diff --git a/anki_vector/screen.py b/anki_vector/screen.py deleted file mode 100644 index 0f4f019..0000000 --- a/anki_vector/screen.py +++ /dev/null @@ -1,194 +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 -# -# https://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's LCD Screen that displays his face. - -The screen is 184 x 96 color (RGB565) pixels. The active area is 23.2mm x 12.1mm. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['dimensions', 'convert_image_to_screen_data', - 'convert_pixels_to_screen_data', 'ScreenComponent'] - -import sys - -from . import color, connection, util -from .messaging import protocol - -try: - from PIL import Image -except ImportError: - sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install") - -SCREEN_WIDTH = 184 -SCREEN_HEIGHT = 96 - - -def dimensions(): - """Return the dimension (width, height) of the Screen. - - .. testcode:: - - import anki_vector - - screen_dimensions = anki_vector.screen.SCREEN_WIDTH, anki_vector.screen.SCREEN_HEIGHT - - Returns: - A tuple of ints (width, height) - """ - return SCREEN_WIDTH, SCREEN_HEIGHT - - -def convert_pixels_to_screen_data(pixel_data: list, image_width: int, image_height: int): - """Convert a sequence of pixel data to the correct format to display on Vector's face. - - :param pixel_data: sequence of triplets representing rgb values, should be ints from 0-255 - :param image_width: width of the image defined by the pixel_data - :param image_height: height of the image defined by the pixel_data - - .. testcode:: - - import anki_vector - from anki_vector.screen import convert_pixels_to_screen_data - from PIL import Image - - image_file = Image.open('../examples/face_images/cozmo_image.jpg') - image_data = image_file.getdata() - pixel_bytes = convert_pixels_to_screen_data(image_data, image_file.width, image_file.height) - - Returns: - A :class:`bytes` object representing all of the pixels (16bit color in rgb565 format) - - Raises: - ValueError: Invalid Dimensions - ValueError: Bad image_width - ValueError: Bad image_height - """ - if len(pixel_data) != (image_width * image_height): - raise ValueError('Invalid Dimensions: len(pixel_data) {0} != image_width={1} * image_height={2} (== {3})'. format(len(pixel_data), - image_width, - image_height, - image_width * - image_height)) - - # @TODO: We should decide on a resampling approach and have this function automatically rescale images - # We should either enforce the aspect ratio, or have options to: - # - automatically crop to the proper aspect ratio - # - stretch to fit - # - shrink to fit with margins some default color - if image_width != SCREEN_WIDTH: - raise ValueError('Bad image_width: image_width {0} must be the resolution width: {1}'. format(image_width, SCREEN_WIDTH)) - - if image_height != SCREEN_HEIGHT: - raise ValueError('Bad image_height: image_height {0} must be the resolution height: {1}'. format(image_height, SCREEN_HEIGHT)) - - color_565_data = [] - for color_tuple in pixel_data: - color_object = color.Color(rgb=color_tuple) - color_565_data.extend(color_object.rgb565_bytepair) - - return bytes(color_565_data) - - -def convert_image_to_screen_data(pil_image: Image.Image): - """Convert an image into the correct format to display on Vector's face. - - .. testcode:: - - import anki_vector - - try: - from PIL import Image - except ImportError: - sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install") - - with anki_vector.Robot() as robot: - # Load an image - image_file = Image.open('../examples/face_images/cozmo_image.jpg') - - # Convert the image to the format used by the Screen - screen_data = anki_vector.screen.convert_image_to_screen_data(image_file) - robot.screen.set_screen_with_image_data(screen_data, 4.0) - - :param pil_image: The image to display on Vector's face - - Returns: - A :class:`bytes` object representing all of the pixels (16bit color in rgb565 format) - """ - image_data = pil_image.getdata() - - return convert_pixels_to_screen_data(image_data, pil_image.width, pil_image.height) - - -class ScreenComponent(util.Component): - """Handles messaging to control Vector's screen""" - - @connection.on_connection_thread(log_messaging=False) - async def set_screen_with_image_data(self, image_data: bytes, duration_sec: float, interrupt_running: bool = True): - """ - Display an image on Vector's Screen (his "face"). - - .. testcode:: - - import anki_vector - - try: - from PIL import Image - except ImportError: - sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install") - - with anki_vector.Robot() as robot: - # Load an image - image_file = Image.open('../examples/face_images/cozmo_image.jpg') - - # Convert the image to the format used by the Screen - screen_data = anki_vector.screen.convert_image_to_screen_data(image_file) - robot.screen.set_screen_with_image_data(screen_data, 4.0) - - :param image_data: A :class:`bytes` object representing all of the pixels (16bit color in rgb565 format) - :param duration_sec: The number of seconds the image should remain on Vector's face. - :param interrupt_running: Set to true so any currently-streaming animation will be aborted in favor of this. - """ - if not isinstance(image_data, bytes): - raise ValueError("set_screen_with_image_data expected bytes") - if len(image_data) != 35328: - raise ValueError("set_screen_with_image_data expected 35328 bytes - (2 bytes each for 17664 pixels)") - - # Generate the message - message = protocol.DisplayFaceImageRGBRequest() - # Create byte array at the Screen resolution - message.face_data = image_data - message.duration_ms = int(1000 * duration_sec) - message.interrupt_running = interrupt_running - - return await self.grpc_interface.DisplayFaceImageRGB(message) - - def set_screen_to_color(self, solid_color: color.Color, duration_sec: float, interrupt_running: bool = True): - """ - Set Vector's Screen (his "face"). to a solid color. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.screen.set_screen_to_color(anki_vector.color.Color(rgb=[255, 128, 0]), duration_sec=1.0) - - :param solid_color: Desired color to set Vector's Screen. - :param duration_sec: The number of seconds the color should remain on Vector's face. - :param interrupt_running: Set to true so any currently-streaming animation will be aborted in favor of this. - """ - image_data = bytes(solid_color.rgb565_bytepair * 17664) - return self.set_screen_with_image_data(image_data, duration_sec, interrupt_running) diff --git a/anki_vector/status.py b/anki_vector/status.py deleted file mode 100644 index f3166bb..0000000 --- a/anki_vector/status.py +++ /dev/null @@ -1,314 +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 -# -# https://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/touch.py b/anki_vector/touch.py deleted file mode 100755 index f0faed6..0000000 --- a/anki_vector/touch.py +++ /dev/null @@ -1,119 +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 -# -# https://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. - -""" -Support for Vector's touch sensor. - -The robot will forward a raw sensor reading representing the capacitance detected -on its back sensor. Accompanied with this value is a true/false flag that takes into -account other aspects of the robot's state to evaluate whether the robot thinks it is -being touched or not. This flag is the same value used internally for petting detection. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ["TouchComponent", "TouchSensorData"] - -from . import util -from .events import Events -from .messaging import protocol - - -class TouchSensorData: - """A touch sample from the capacitive touch sensor, accompanied with the robot's - conclusion on whether this is considered a valid touch. - """ - - def __init__(self, proto_data: protocol.TouchData): - self._raw_touch_value = proto_data.raw_touch_value - self._is_being_touched = proto_data.is_being_touched - - @property - def raw_touch_value(self) -> int: - """The detected sensitivity from the touch sensor. - - This will not map to a constant raw value, as it may be impacted by various - environmental factors such as whether the robot is on its charger, being held, humidity, etc. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - touch_data = robot.touch.last_sensor_reading - if touch_data is not None: - raw_touch_value = touch_data.raw_touch_value - """ - return self._raw_touch_value - - @property - def is_being_touched(self) -> bool: - """The robot's conclusion on whether the current value is considered - a valid touch. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - touch_data = robot.touch.last_sensor_reading - if touch_data is not None: - is_being_touched = touch_data.is_being_touched - """ - return self._is_being_touched - - -class TouchComponent(util.Component): - """Maintains the most recent touch sensor data - - This will be updated with every broadcast RobotState, and can be queried at any time. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - touch_data = robot.touch.last_sensor_reading - if touch_data is not None: - print('Touch sensor value: {0}, is being touched: {1}'.format(touch_data.raw_touch_value, touch_data.is_being_touched)) - """ - - def __init__(self, robot): - super().__init__(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, - 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) - - @property - def last_sensor_reading(self) -> TouchSensorData: - """:class:`anki_vector.touch.TouchSensorData`: The last reported sensor data. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - touch_data = robot.touch.last_sensor_reading - """ - return self._last_sensor_reading - - def _on_robot_state(self, _, msg): - self._last_sensor_reading = TouchSensorData(msg.touch_data) diff --git a/anki_vector/util.py b/anki_vector/util.py deleted file mode 100644 index e8c4817..0000000 --- a/anki_vector/util.py +++ /dev/null @@ -1,1048 +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 -# -# https://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. - -""" -Utility functions and classes for the Vector SDK. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['Angle', - 'BaseOverlay', - 'Component', - 'Distance', - 'ImageRect', - 'Matrix44', - 'Pose', - 'Position', - 'Quaternion', - 'RectangleOverlay', - 'Speed', - 'Vector2', - 'Vector3', - 'angle_z_to_quaternion', - 'block_while_none', - 'degrees', - 'distance_mm', - 'distance_inches', - 'get_class_logger', - 'parse_command_args', - 'radians', - 'setup_basic_logging', - 'speed_mmps'] - -import argparse -from functools import wraps -import logging -import math -import os -import sys -import time -from typing import Callable - -from .messaging import protocol - -try: - from PIL import Image, ImageDraw -except ImportError: - sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install") - -# TODO Move to the robot class - - -def parse_command_args(parser: argparse.ArgumentParser = None): - """ - Parses command line arguments. - - Attempts to read the robot serial number from the command line arguments. If no serial number - 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:: python - - import anki_vector - - import argparse - - parser = argparse.ArgumentParser() - parser.add_argument("--new_param") - args = anki_vector.util.parse_command_args(parser) - - :param parser: To add new command line arguments, - pass an argparse parser with the new options - already defined. Leave empty to use the defaults. - """ - if parser is None: - parser = argparse.ArgumentParser() - parser.add_argument("-s", "--serial", nargs='?', default=os.environ.get('ANKI_ROBOT_SERIAL', None)) - return parser.parse_args() - - -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. - - :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) - def wrapped(*args, **kwargs): - iterations = 0 - result = func(*args, **kwargs) - while result is None: - time.sleep(interval) - iterations += 1 - if iterations > max_iterations: - raise Exception("Value not ready") - result = func(*args, **kwargs) - return result - return wrapped - return blocker - - -def setup_basic_logging(custom_handler: logging.Handler = None, - general_log_level: str = None, - target: object = None): - """Helper to perform basic setup of the Python logger. - - :param custom_handler: provide an external logger for custom logging locations - :param general_log_level: 'DEBUG', 'INFO', 'WARN', 'ERROR' or an equivalent - constant from the :mod:`logging` module. If None then a - value will be read from the VECTOR_LOG_LEVEL environment variable. - :param target: The stream to send the log data to; defaults to stderr - """ - if general_log_level is None: - general_log_level = os.environ.get('VECTOR_LOG_LEVEL', logging.INFO) - - handler = custom_handler - if handler is None: - handler = logging.StreamHandler(stream=target) - formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s') - handler.setFormatter(formatter) - - vector_logger = logging.getLogger('anki_vector') - if not vector_logger.handlers: - vector_logger.addHandler(handler) - vector_logger.setLevel(general_log_level) - - -def get_class_logger(module: str, obj: object) -> logging.Logger: - """Helper to create logger for a given class (and module). - - .. testcode:: - - import anki_vector - - logger = anki_vector.util.get_class_logger("module_name", "object_name") - - :param module: The name of the module to which the object belongs. - :param obj: the object that owns the logger. - """ - return logging.getLogger(".".join([module, type(obj).__name__])) - - -class Vector2: - """Represents a 2D Vector (type/units aren't specified). - - :param x: X component - :param y: Y component - """ - - __slots__ = ('_x', '_y') - - def __init__(self, x: float, y: float): - self._x = float(x) - self._y = float(y) - - def set_to(self, rhs): - """Copy the x and y components of the given Vector2 instance. - - :param rhs: The right-hand-side of this assignment - the - source Vector2 to copy into this Vector2 instance. - """ - self._x = float(rhs.x) - self._y = float(rhs.y) - - @property - def x(self) -> float: - """The x component.""" - return self._x - - @property - def y(self) -> float: - """The y component.""" - return self._y - - @property - def x_y(self): - """tuple (float, float): The X, Y elements of the Vector2 (x,y)""" - return self._x, self._y - - def __repr__(self): - return "<%s x: %.2f y: %.2f>" % (self.__class__.__name__, self.x, self.y) - - def __add__(self, other): - if not isinstance(other, Vector2): - raise TypeError("Unsupported operand for + expected Vector2") - return Vector2(self.x + other.x, self.y + other.y) - - def __sub__(self, other): - if not isinstance(other, Vector2): - raise TypeError("Unsupported operand for - expected Vector2") - return Vector2(self.x - other.x, self.y - other.y) - - def __mul__(self, other): - if not isinstance(other, (int, float)): - raise TypeError("Unsupported operand for * expected number") - return Vector2(self.x * other, self.y * other) - - def __truediv__(self, other): - if not isinstance(other, (int, float)): - raise TypeError("Unsupported operand for / expected number") - return Vector2(self.x / other, self.y / other) - - -class Vector3: - """Represents a 3D Vector (type/units aren't specified). - - :param x: X component - :param y: Y component - :param z: Z component - """ - - __slots__ = ('_x', '_y', '_z') - - def __init__(self, x: float, y: float, z: float): - self._x = float(x) - self._y = float(y) - self._z = float(z) - - def set_to(self, rhs): - """Copy the x, y and z components of the given Vector3 instance. - - :param rhs: The right-hand-side of this assignment - the - source Vector3 to copy into this Vector3 instance. - """ - self._x = float(rhs.x) - self._y = float(rhs.y) - self._z = float(rhs.z) - - @property - def x(self) -> float: - """The x component.""" - return self._x - - @property - def y(self) -> float: - """The y component.""" - return self._y - - @property - def z(self) -> float: - """The z component.""" - return self._z - - @property - def magnitude_squared(self) -> float: - """float: The magnitude of the Vector3 instance""" - return self._x**2 + self._y**2 + self._z**2 - - @property - def magnitude(self) -> float: - """The magnitude of the Vector3 instance""" - return math.sqrt(self.magnitude_squared) - - @property - def normalized(self): - """A Vector3 instance with the same direction and unit magnitude""" - mag = self.magnitude - if mag == 0: - return Vector3(0, 0, 0) - return Vector3(self._x / mag, self._y / mag, self._z / mag) - - def dot(self, other): - """The dot product of this and another Vector3 instance""" - if not isinstance(other, Vector3): - raise TypeError("Unsupported argument for dot product, expected Vector3") - return self._x * other.x + self._y * other.y + self._z * other.z - - def cross(self, other): - """The cross product of this and another Vector3 instance""" - if not isinstance(other, Vector3): - raise TypeError("Unsupported argument for cross product, expected Vector3") - - return Vector3( - self._y * other.z - self._z * other.y, - self._z * other.x - self._x * other.z, - self._x * other.y - self._y * other.x) - - @property - def x_y_z(self): - """tuple (float, float, float): The X, Y, Z elements of the Vector3 (x,y,z)""" - return self._x, self._y, self._z - - def __repr__(self): - return f"<{self.__class__.__name__} x: {self.x:.2f} y: {self.y:.2f} z: {self.z:.2f}>" - - def __add__(self, other): - if not isinstance(other, Vector3): - raise TypeError("Unsupported operand for +, expected Vector3") - return Vector3(self.x + other.x, self.y + other.y, self.z + other.z) - - def __sub__(self, other): - if not isinstance(other, Vector3): - raise TypeError("Unsupported operand for -, expected Vector3") - return Vector3(self.x - other.x, self.y - other.y, self.z - other.z) - - def __mul__(self, other): - if not isinstance(other, (int, float)): - raise TypeError("Unsupported operand for * expected number") - return Vector3(self.x * other, self.y * other, self.z * other) - - def __truediv__(self, other): - if not isinstance(other, (int, float)): - raise TypeError("Unsupported operand for / expected number") - return Vector3(self.x / other, self.y / other, self.z / other) - - -class Angle: - """Represents an angle. - - Use the :func:`degrees` or :func:`radians` convenience methods to generate - an Angle instance. - - :param radians: The number of radians the angle should represent - (cannot be combined with ``degrees``) - :param degrees: The number of degress the angle should represent - (cannot be combined with ``radians``) - """ - - __slots__ = ('_radians') - - def __init__(self, radians: float = None, degrees: float = None): # pylint: disable=redefined-outer-name - if radians is None and degrees is None: - raise ValueError("Expected either the degrees or radians keyword argument") - if radians and degrees: - raise ValueError("Expected either the degrees or radians keyword argument, not both") - - if degrees is not None: - radians = degrees * math.pi / 180 - self._radians = float(radians) - - @property - def radians(self) -> float: # pylint: disable=redefined-outer-name - """The angle in radians.""" - return self._radians - - @property - def degrees(self) -> float: # pylint: disable=redefined-outer-name - """The angle in degrees.""" - return self._radians / math.pi * 180 - - def __repr__(self): - return f"<{self.__class__.__name__} Radians: {self.radians:.2f} Degrees: {self.degrees:.2f}>" - - def __add__(self, other): - if not isinstance(other, Angle): - raise TypeError("Unsupported type for + expected Angle") - return Angle(radians=(self.radians + other.radians)) - - def __sub__(self, other): - if not isinstance(other, Angle): - raise TypeError("Unsupported type for - expected Angle") - return Angle(radians=(self.radians - other.radians)) - - def __mul__(self, other): - if not isinstance(other, (int, float)): - 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. - - :param angle_z: The z axis angle. - - Returns: - q0, q1, q2, q3 (float, float, float, float): A tuple with all the members - of a quaternion defined by angle_z. - """ - - # Define the quaternion to be converted from a Euler angle (x,y,z) of 0,0,angle_z - # These equations have their original equations above, and simplified implemented - # q0 = cos(x/2)*cos(y/2)*cos(z/2) + sin(x/2)*sin(y/2)*sin(z/2) - q0 = math.cos(angle_z.radians / 2) - # q1 = sin(x/2)*cos(y/2)*cos(z/2) - cos(x/2)*sin(y/2)*sin(z/2) - q1 = 0 - # q2 = cos(x/2)*sin(y/2)*cos(z/2) + sin(x/2)*cos(y/2)*sin(z/2) - q2 = 0 - # q3 = cos(x/2)*cos(y/2)*sin(z/2) - sin(x/2)*sin(y/2)*cos(z/2) - q3 = math.sin(angle_z.radians / 2) - return q0, q1, q2, q3 - - -def degrees(degrees: float) -> Angle: # pylint: disable=redefined-outer-name - """An Angle instance set to the specified number of degrees.""" - return Angle(degrees=degrees) - - -def radians(radians: float) -> Angle: # pylint: disable=redefined-outer-name - """An Angle instance set to the specified number of radians.""" - return Angle(radians=radians) - - -class Matrix44: - """A 4x4 Matrix for representing the rotation and/or position of an object in the world. - - Can be generated from a :class:`Quaternion` for a pure rotation matrix, or - combined with a position for a full translation matrix, as done by - :meth:`Pose.to_matrix`. - """ - __slots__ = ('m00', 'm10', 'm20', 'm30', - 'm01', 'm11', 'm21', 'm31', - 'm02', 'm12', 'm22', 'm32', - 'm03', 'm13', 'm23', 'm33') - - def __init__(self, - m00: float, m10: float, m20: float, m30: float, - m01: float, m11: float, m21: float, m31: float, - m02: float, m12: float, m22: float, m32: float, - m03: float, m13: float, m23: float, m33: float): - self.m00 = float(m00) - self.m10 = float(m10) - self.m20 = float(m20) - self.m30 = float(m30) - - self.m01 = float(m01) - self.m11 = float(m11) - self.m21 = float(m21) - self.m31 = float(m31) - - self.m02 = float(m02) - self.m12 = float(m12) - self.m22 = float(m22) - self.m32 = float(m32) - - self.m03 = float(m03) - self.m13 = float(m13) - self.m23 = float(m23) - self.m33 = float(m33) - - def __repr__(self): - return ("<%s: " - "%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f " - "%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f>" % ( - self.__class__.__name__, *self.in_row_order)) - - @property - def tabulated_string(self) -> str: - """A multi-line string formatted with tabs to show the matrix contents.""" - return ("%.1f\t%.1f\t%.1f\t%.1f\n" - "%.1f\t%.1f\t%.1f\t%.1f\n" - "%.1f\t%.1f\t%.1f\t%.1f\n" - "%.1f\t%.1f\t%.1f\t%.1f" % self.in_row_order) - - @property - def in_row_order(self): - """tuple of 16 floats: The contents of the matrix in row order.""" - return self.m00, self.m01, self.m02, self.m03,\ - self.m10, self.m11, self.m12, self.m13,\ - self.m20, self.m21, self.m22, self.m23,\ - self.m30, self.m31, self.m32, self.m33 - - @property - def in_column_order(self): - """tuple of 16 floats: The contents of the matrix in column order.""" - return self.m00, self.m10, self.m20, self.m30,\ - self.m01, self.m11, self.m21, self.m31,\ - self.m02, self.m12, self.m22, self.m32,\ - self.m03, self.m13, self.m23, self.m33 - - @property - def forward_xyz(self): - """tuple of 3 floats: The x,y,z components representing the matrix's forward vector.""" - return self.m00, self.m01, self.m02 - - @property - def left_xyz(self): - """tuple of 3 floats: The x,y,z components representing the matrix's left vector.""" - return self.m10, self.m11, self.m12 - - @property - def up_xyz(self): - """tuple of 3 floats: The x,y,z components representing the matrix's up vector.""" - return self.m20, self.m21, self.m22 - - @property - def pos_xyz(self): - """tuple of 3 floats: The x,y,z components representing the matrix's position vector.""" - return self.m30, self.m31, self.m32 - - def set_forward(self, x: float, y: float, z: float): - """Set the x,y,z components representing the matrix's forward vector. - - :param x: The X component. - :param y: The Y component. - :param z: The Z component. - """ - self.m00 = float(x) - self.m01 = float(y) - self.m02 = float(z) - - def set_left(self, x: float, y: float, z: float): - """Set the x,y,z components representing the matrix's left vector. - - :param x: The X component. - :param y: The Y component. - :param z: The Z component. - """ - self.m10 = float(x) - self.m11 = float(y) - self.m12 = float(z) - - def set_up(self, x: float, y: float, z: float): - """Set the x,y,z components representing the matrix's up vector. - - :param x: The X component. - :param y: The Y component. - :param z: The Z component. - """ - self.m20 = float(x) - self.m21 = float(y) - self.m22 = float(z) - - def set_pos(self, x: float, y: float, z: float): - """Set the x,y,z components representing the matrix's position vector. - - :param x: The X component. - :param y: The Y component. - :param z: The Z component. - """ - self.m30 = float(x) - self.m31 = float(y) - self.m32 = float(z) - - -class Quaternion: - """Represents the rotation of an object in the world.""" - - __slots__ = ('_q0', '_q1', '_q2', '_q3') - - def __init__(self, q0: float = None, q1: float = None, q2: float = None, q3: float = None, angle_z: Angle = None): - is_quaternion = q0 is not None and q1 is not None and q2 is not None and q3 is not None - - if not is_quaternion and angle_z is None: - raise ValueError("Expected either the q0 q1 q2 and q3 or angle_z keyword arguments") - if is_quaternion and angle_z: - raise ValueError("Expected either the q0 q1 q2 and q3 or angle_z keyword argument," - "not both") - if angle_z is not None: - if not isinstance(angle_z, Angle): - raise TypeError("Unsupported type for angle_z expected Angle") - q0, q1, q2, q3 = angle_z_to_quaternion(angle_z) - - self._q0 = float(q0) - self._q1 = float(q1) - self._q2 = float(q2) - self._q3 = float(q3) - - @property - def q0(self) -> float: - """The q0 (w) value of the quaternion.""" - return self._q0 - - @property - def q1(self) -> float: - """The q1 (i) value of the quaternion.""" - return self._q1 - - @property - def q2(self) -> float: - """The q2 (j) value of the quaternion.""" - return self._q2 - - @property - def q3(self) -> float: - """The q3 (k) value of the quaternion.""" - return self._q3 - - @property - def angle_z(self) -> Angle: - """An Angle instance representing the z Euler component of the object's rotation. - - Defined as the rotation in the z axis. - """ - q0, q1, q2, q3 = self.q0_q1_q2_q3 - return Angle(radians=math.atan2(2 * (q1 * q2 + q0 * q3), 1 - 2 * (q2**2 + q3**2))) - - @property - def q0_q1_q2_q3(self): - """tuple of float: Contains all elements of the quaternion (q0,q1,q2,q3)""" - return self._q0, self._q1, self._q2, self._q3 - - def to_matrix(self, pos_x: float = 0.0, pos_y: float = 0.0, pos_z: float = 0.0): - """Convert the Quaternion to a 4x4 matrix representing this rotation. - - A position can also be provided to generate a full translation matrix. - - :param pos_x: The x component for the position. - :param pos_y: The y component for the position. - :param pos_z: The z component for the position. - - Returns: - :class:`anki_vector.util.Matrix44`: A matrix representing this Quaternion's - rotation, with the provided position (which defaults to 0,0,0). - """ - # See https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation - q0q0 = self.q0 * self.q0 - q1q1 = self.q1 * self.q1 - q2q2 = self.q2 * self.q2 - q3q3 = self.q3 * self.q3 - - q0x2 = self.q0 * 2.0 # saves 2 multiplies - q0q1x2 = q0x2 * self.q1 - q0q2x2 = q0x2 * self.q2 - q0q3x2 = q0x2 * self.q3 - q1x2 = self.q1 * 2.0 # saves 1 multiply - q1q2x2 = q1x2 * self.q2 - q1q3x2 = q1x2 * self.q3 - q2q3x2 = 2.0 * self.q2 * self.q3 - - m00 = (q0q0 + q1q1 - q2q2 - q3q3) - m01 = (q1q2x2 + q0q3x2) - m02 = (q1q3x2 - q0q2x2) - - m10 = (q1q2x2 - q0q3x2) - m11 = (q0q0 - q1q1 + q2q2 - q3q3) - m12 = (q0q1x2 + q2q3x2) - - m20 = (q0q2x2 + q1q3x2) - m21 = (q2q3x2 - q0q1x2) - m22 = (q0q0 - q1q1 - q2q2 + q3q3) - - return Matrix44(m00, m10, m20, float(pos_x), - m01, m11, m21, float(pos_y), - m02, m12, m22, float(pos_z), - 0.0, 0.0, 0.0, 1.0) - - def __repr__(self): - return (f"<{self.__class__.__name__} q0: {self.q0:.2f} q1: {self.q1:.2f}" - f" q2: {self.q2:.2f} q3: {self.q3:.2f} {self.angle_z}>") - - -class Position(Vector3): - """Represents the position of an object in the world. - - A position consists of its x, y and z values in millimeters. - - :param x: X position in millimeters - :param y: Y position in millimeters - :param z: Z position in millimeters - """ - __slots__ = () - - -class Pose: - """Represents where an object is in the world. - - Whenever Vector is delocalized (i.e. whenever Vector no longer knows - where he is - e.g. when he's picked up), Vector creates a new pose starting at - (0,0,0) with no rotation, with origin_id incremented to show that these poses - cannot be compared with earlier ones. As Vector drives around, his pose (and the - pose of other objects he observes - e.g. faces, his LightCube, charger, etc.) is relative to this - initial position and orientation. - - The coordinate space is relative to Vector, where Vector's origin is the - point on the ground between Vector's two front wheels. The X axis is Vector's forward direction, - the Y axis is to Vector's left, and the Z axis is up. - - Only poses of the same origin_id can safely be compared or operated on. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, Pose - - with anki_vector.Robot() as robot: - pose = Pose(x=50, y=0, z=0, angle_z=anki_vector.util.Angle(degrees=0)) - robot.behavior.go_to_pose(pose) - """ - __slots__ = ('_position', '_rotation', '_origin_id') - - def __init__(self, x: float, y: float, z: float, q0: float = None, q1: float = None, q2: float = None, q3: float = None, - angle_z: Angle = None, origin_id: int = -1): - self._position = Position(x, y, z) - self._rotation = Quaternion(q0, q1, q2, q3, angle_z) - self._origin_id = origin_id - - @property - def position(self) -> Position: - """The position component of this pose.""" - return self._position - - @property - def rotation(self) -> Quaternion: - """The rotation component of this pose.""" - return self._rotation - - @property - def origin_id(self) -> int: - """An ID maintained by the robot which represents which coordinate frame this pose is in.""" - return self._origin_id - - def __repr__(self): - return (f"<{self.__class__.__name__}: {self._position}" - f" {self._rotation} >") - - def define_pose_relative_this(self, new_pose): - """Creates a new pose such that new_pose's origin is now at the location of this pose. - - :param anki_vector.util.Pose new_pose: The pose which origin is being changed. - - Returns: - A :class:`anki_vector.util.Pose` object for which the origin was this pose's origin. - """ - if not isinstance(new_pose, Pose): - raise TypeError("Unsupported type for new_origin, must be of type Pose") - x, y, z = self.position.x_y_z - angle_z = self.rotation.angle_z - new_x, new_y, new_z = new_pose.position.x_y_z - new_angle_z = new_pose.rotation.angle_z - - cos_angle = math.cos(angle_z.radians) - sin_angle = math.sin(angle_z.radians) - res_x = x + (cos_angle * new_x) - (sin_angle * new_y) - res_y = y + (sin_angle * new_x) + (cos_angle * new_y) - res_z = z + new_z - res_angle = angle_z + new_angle_z - return Pose(res_x, - res_y, - res_z, - angle_z=res_angle, - origin_id=self._origin_id) - - @property - def is_valid(self) -> bool: - """True if this is a valid, usable pose.""" - return self.origin_id >= 0 - - def is_comparable(self, other_pose) -> bool: - """Checks whether these two poses are comparable. - - Poses are comparable if they're valid and having matching origin IDs. - - :param other_pose: The other pose to compare against. Type is Pose. - - Returns: - True if the two poses are comparable, False otherwise. - """ - return (self.is_valid and other_pose.is_valid and - (self.origin_id == other_pose.origin_id)) - - def to_matrix(self) -> Matrix44: - """Convert the Pose to a Matrix44. - - Returns: - A matrix representing this Pose's position and rotation. - """ - return self.rotation.to_matrix(*self.position.x_y_z) - - def to_proto_pose_struct(self) -> protocol.PoseStruct: - """Converts the Pose into the robot's messaging pose format. - """ - return protocol.PoseStruct( - x=self._position.x, - y=self._position.y, - z=self._position.z, - q0=self._rotation.q0, - q1=self._rotation.q1, - q2=self._rotation.q2, - q3=self._rotation.q3, - origin_id=self._origin_id) - - -class ImageRect: - """Image coordinates and size""" - - __slots__ = ('_x_top_left', '_y_top_left', '_width', '_height') - - def __init__(self, x_top_left: float, y_top_left: float, width: float, height: float): - self._x_top_left = float(x_top_left) - self._y_top_left = float(y_top_left) - self._width = float(width) - self._height = float(height) - - @property - def x_top_left(self) -> float: - """The top left x value of where the object was last visible within Vector's camera view.""" - return self._x_top_left - - @property - def y_top_left(self) -> float: - """The top left y value of where the object was last visible within Vector's camera view.""" - return self._y_top_left - - @property - def width(self) -> float: - """The width of the object from when it was last visible within Vector's camera view.""" - return self._width - - @property - def height(self) -> float: - """The height of the object from when it was last visible within Vector's camera view.""" - return self._height - - -class Distance: - """Represents a distance. - - The class allows distances to be returned in either millimeters or inches. - - Use the :func:`distance_inches` or :func:`distance_mm` convenience methods to generate - a Distance instance. - - :param distance_mm: The number of millimeters the distance should - represent (cannot be combined with ``distance_inches``). - :param distance_inches: The number of inches the distance should - represent (cannot be combined with ``distance_mm``). - """ - - __slots__ = ('_distance_mm') - - def __init__(self, distance_mm: float = None, distance_inches: float = None): # pylint: disable=redefined-outer-name - if distance_mm is None and distance_inches is None: - raise ValueError("Expected either the distance_mm or distance_inches keyword argument") - if distance_mm and distance_inches: - raise ValueError("Expected either the distance_mm or distance_inches keyword argument, not both") - - if distance_inches is not None: - distance_mm = distance_inches * 25.4 - self._distance_mm = float(distance_mm) - - def __repr__(self): - return "<%s %.2f mm (%.2f inches)>" % (self.__class__.__name__, self.distance_mm, self.distance_inches) - - def __add__(self, other): - if not isinstance(other, Distance): - raise TypeError("Unsupported operand for + expected Distance") - return distance_mm(self.distance_mm + other.distance_mm) - - def __sub__(self, other): - if not isinstance(other, Distance): - raise TypeError("Unsupported operand for - expected Distance") - return distance_mm(self.distance_mm - other.distance_mm) - - def __mul__(self, other): - if not isinstance(other, (int, float)): - raise TypeError("Unsupported operand for * expected number") - return distance_mm(self.distance_mm * other) - - def __truediv__(self, other): - if not isinstance(other, (int, float)): - raise TypeError("Unsupported operand for / expected number") - return distance_mm(self.distance_mm / other) - - @property - def distance_mm(self) -> float: # pylint: disable=redefined-outer-name - """The distance in millimeters""" - return self._distance_mm - - @property - def distance_inches(self) -> float: # pylint: disable=redefined-outer-name - return self._distance_mm / 25.4 - - -def distance_mm(distance_mm: float): # pylint: disable=redefined-outer-name - """Returns an :class:`anki_vector.util.Distance` instance set to the specified number of millimeters.""" - return Distance(distance_mm=distance_mm) - - -def distance_inches(distance_inches: float): # pylint: disable=redefined-outer-name - """Returns an :class:`anki_vector.util.Distance` instance set to the specified number of inches.""" - return Distance(distance_inches=distance_inches) - - -class Speed: - """Represents a speed. - - This class allows speeds to be measured in millimeters per second. - - Use :func:`speed_mmps` convenience methods to generate - a Speed instance. - - :param speed_mmps: The number of millimeters per second the speed - should represent. - """ - - __slots__ = ('_speed_mmps') - - def __init__(self, speed_mmps: float = None): # pylint: disable=redefined-outer-name - if speed_mmps is None: - raise ValueError("Expected speed_mmps keyword argument") - self._speed_mmps = float(speed_mmps) - - def __repr__(self): - return "<%s %.2f mmps>" % (self.__class__.__name__, self.speed_mmps) - - def __add__(self, other): - if not isinstance(other, Speed): - raise TypeError("Unsupported operand for + expected Speed") - return speed_mmps(self.speed_mmps + other.speed_mmps) - - def __sub__(self, other): - if not isinstance(other, Speed): - raise TypeError("Unsupported operand for - expected Speed") - return speed_mmps(self.speed_mmps - other.speed_mmps) - - def __mul__(self, other): - if not isinstance(other, (int, float)): - raise TypeError("Unsupported operand for * expected number") - return speed_mmps(self.speed_mmps * other) - - def __truediv__(self, other): - if not isinstance(other, (int, float)): - raise TypeError("Unsupported operand for / expected number") - return speed_mmps(self.speed_mmps / other) - - @property - def speed_mmps(self: float) -> float: # pylint: disable=redefined-outer-name - """The speed in millimeters per second (mmps).""" - return self._speed_mmps - - -def speed_mmps(speed_mmps: float): # pylint: disable=redefined-outer-name - """:class:`anki_vector.util.Speed` instance set to the specified millimeters per second speed.""" - return Speed(speed_mmps=speed_mmps) - - -class BaseOverlay: - """A base overlay is used as a base class for other forms of overlays that can be drawn on top of an image. - - :param line_thickness: The thickness of the line being drawn. - :param line_color: The color of the line to be drawn. - """ - - def __init__(self, line_thickness: int, line_color: tuple): - self._line_thickness: int = line_thickness - self._line_color: tuple = line_color - - @property - def line_thickness(self) -> int: - """The thickness of the line being drawn.""" - return self._line_thickness - - @property - def line_color(self) -> tuple: - """The color of the line to be drawn.""" - return self._line_color - - -class RectangleOverlay(BaseOverlay): - """A rectangle that can be drawn on top of a given image. - - :param width: The width of the rectangle to be drawn. - :param height: The height of the rectangle to be drawn. - :param line_thickness: The thickness of the line being drawn. - :param line_color: The color of the line to be drawn. - """ - - # @TODO Implement overlay using an ImageRect rather than a raw width & height - def __init__(self, width: int, height: int, line_thickness: int = 5, line_color: tuple = (255, 0, 0)): - super().__init__(line_thickness, line_color) - self._width: int = width - self._height: int = height - - @property - def width(self) -> int: - """The width of the rectangle to be drawn.""" - return self._width - - @property - def height(self) -> int: - """The height of the rectangle to be drawn.""" - return self._height - - def apply_overlay(self, image: Image.Image) -> None: - """Draw a rectangle on top of the given image.""" - d = ImageDraw.Draw(image) - - image_width, image_height = image.size - remaining_width = image_width - self.width - remaining_height = image_height - self.height - x1, y1 = remaining_height // 2, remaining_width // 2 - x2, y2 = (image_height - (remaining_height // 2)), (image_width - (remaining_width // 2)) - - for i in range(0, self.line_thickness): - d.rectangle([x1 + i, y1 + i, x2 - i, y2 - i], outline=self.line_color) - - -class Component: - """ Base class for all components.""" - - def __init__(self, robot): - self.logger = get_class_logger(__name__, self) - self._robot = robot - - @property - def robot(self): - return self._robot - - @property - def conn(self): - return self._robot.conn - - @property - def force_async(self): - return self._robot.force_async - - @property - def grpc_interface(self): - """A direct reference to the connected aiogrpc interface. - """ - return self._robot.conn.grpc_interface diff --git a/anki_vector/version.py b/anki_vector/version.py deleted file mode 100644 index 829dce5..0000000 --- a/anki_vector/version.py +++ /dev/null @@ -1,15 +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 -# -# https://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. - -__version__ = "0.5.2.dev0" diff --git a/anki_vector/viewer.py b/anki_vector/viewer.py deleted file mode 100644 index ed2e1f0..0000000 --- a/anki_vector/viewer.py +++ /dev/null @@ -1,428 +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 -# -# https://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. - -"""Displays camera feed from Vector's camera. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['ViewerComponent', 'Viewer3DComponent'] - -import multiprocessing as mp -import os -import sys -import threading - -try: - import cv2 -except ImportError as exc: - sys.exit("Cannot import opencv-python: Do `pip3 install opencv-python` to install") - -try: - import numpy as np -except ImportError as exc: - sys.exit("Cannot import numpy: Do `pip3 install numpy` to install") - -try: - from PIL import Image -except ImportError: - sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install") - -from . import util -from .events import Events - - -class ViewerComponent(util.Component): - """This component opens a window and renders the images obtained from Vector's camera. - This viewer window is run in a separate process spawned by :func:`~ViewerComponent.show_video`. - Being on a separate process means the rendering of the camera does not block the main thread - of the calling code, and allows the viewer to have its own ui thread which it can operate on. - :func:`~ViewerComponent.stop_video` will stop the viewer process. - - .. testcode:: - - import anki_vector - - import time - - with anki_vector.Robot(enable_camera_feed=True, show_viewer=True) as robot: - time.sleep(5) - - :param robot: A reference to the owner Robot object. (May be :class:`None`) - """ - - def __init__(self, robot): - super().__init__(robot) - self.overlays: list = [] - self._close_event: mp.Event = None - self._frame_queue: mp.Queue = None - self._process = None - - def show_video(self, timeout: float = 10.0) -> None: - """Render a video stream using the images obtained from - Vector's camera feed. - - Be sure to create your Robot object with the camera feed enabled - by using "show_viewer=True" and "enable_camera_feed=True". - - .. testcode:: - - import anki_vector - import time - - with anki_vector.Robot(enable_camera_feed=True) as robot: - robot.viewer.show_video() - time.sleep(10) - - :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._close_event, - self.overlays, - timeout), - daemon=True, - name="Camera Viewer Process") - self._process.start() - - def stop_video(self) -> None: - """Stop rendering video of Vector's camera feed and close the viewer process. - - .. testcode:: - - import anki_vector - import time - - with anki_vector.Robot(show_viewer=True) as robot: - time.sleep(10) - robot.viewer.stop_video() - """ - if self._close_event: - self._close_event.set() - self._close_event = None - if self._frame_queue: - try: - self._frame_queue.put(None, False) - except mp.queues.Full: - pass - self._frame_queue = None - if self._process: - self._process.join(timeout=5) - if self._process.is_alive(): - self._process.terminate() - self._process = None - - def enqueue_frame(self, image: Image.Image): - """Sends a frame to the viewer's rendering process. Sending `None` to the viewer - will cause it to gracefully shutdown. - - .. note:: - - This function will be called automatically from the camera feed when the - :class:`~anki_vector.robot.Robot` object is created with ``enable_camera_feed=True``. - - .. code-block:: python - - import anki_vector - from PIL.Image import Image - - image = Image() - with anki_vector.Robot(show_viewer=True) as robot: - robot.viewer.enqueue_frame(image) - - :param image: A frame from Vector's camera. - """ - 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: - pass - - def _apply_overlays(self, image: Image.Image) -> None: - """Apply all overlays attached to viewer instance on to image from camera feed.""" - for overlay in self.overlays: - overlay.apply_overlay(image) - return image - - @staticmethod - 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. - - :param queue: A queue to send frames between main thread and other process. - :param overlays: overlays to be drawn on the images of the renderer. - :param timeout: The time without a new frame before the process will exit. - """ - is_windows = os.name == 'nt' - window_name = "Vector Camera Feed" - cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) - try: - image = queue.get(True, timeout=timeout) - while image: - if event.is_set(): - break - if overlays: - for overlay in overlays: - overlay.apply_overlay(image) - image = cv2.cvtColor(np.array(image), cv2.COLOR_BGR2RGB) - cv2.imshow(window_name, image) - cv2.waitKey(1) - if not is_windows and cv2.getWindowProperty(window_name, cv2.WND_PROP_VISIBLE) < 1: - break - image = queue.get(True, timeout=timeout) - except TimeoutError: - pass - except KeyboardInterrupt: - pass - finally: - event.set() - cv2.destroyWindow(window_name) - cv2.waitKey(1) - - -class _ExternalRenderCallFunctor(): # pylint: disable=too-few-public-methods - """Externally specified OpenGL render function. - - Allows extra geometry to be rendered into OpenGLViewer. - - :param f: function to call inside the rendering loop - :param f_args: a list of arguments to supply to the callable function - """ - - def __init__(self, f: callable, f_args: list): - self._f = f - self._f_args = f_args - - def invoke(self, user_data_queue): - """Calls the internal function""" - self._f(*self._f_args, user_data_queue=user_data_queue) - - -class Viewer3DComponent(util.Component): - """This component opens a window and renders the a 3D view obtained from Vector's navigation map. - This viewer window is run in a separate process spawned by :func:`~Viewer3DComponent.show`. - Being on a separate process means the rendering of the 3D view does not block the main thread - of the calling code, and allows the viewer to have its own ui thread with which it can render OpenGL. - :func:`~Viewer3DComponent.close` will stop the viewer process. - - .. testcode:: - - import anki_vector - - import time - - with anki_vector.Robot(enable_nav_map_feed=True, show_3d_viewer=True) as robot: - time.sleep(5) - - :param robot: A reference to the owner Robot object. (May be :class:`None`) - """ - - def __init__(self, robot): - super().__init__(robot) - self.overlays: list = [] - self._close_event: mp.Event = None - self._input_intent_queue: mp.Queue = None - self._nav_map_queue: mp.Queue = None - self._world_frame_queue: mp.Queue = None - self._extra_render_function_queue: mp.Queue = None - self._user_data_queue: mp.Queue = None - self._process: mp.process.BaseProcess = None - self._update_thread: threading.Thread = None - self._last_robot_control_intents = None - - def show(self): - """Spawns a background process that shows the navigation map in a 3D view. - - .. testcode:: - - import anki_vector - - import time - - with anki_vector.Robot(enable_nav_map_feed=True) as robot: - robot.viewer_3d.show() - time.sleep(5) - robot.viewer_3d.close() - """ - from . import opengl - ctx = mp.get_context('spawn') - self._close_event = ctx.Event() - self._input_intent_queue = ctx.Queue(maxsize=10) - self._nav_map_queue = ctx.Queue(maxsize=10) - self._world_frame_queue = ctx.Queue(maxsize=10) - self._extra_render_function_queue = ctx.Queue(maxsize=1) - self._user_data_queue = ctx.Queue() - self._update_thread = threading.Thread(target=self._update, - args=(), - daemon=True, - name="3D Viewer Update Thread") - self._update_thread.start() - self._process = ctx.Process(target=opengl.main, - args=(self._close_event, - self._input_intent_queue, - self._nav_map_queue, - self._world_frame_queue, - self._extra_render_function_queue, - self._user_data_queue), - daemon=True, - name="3D Viewer Process") - self._process.start() - self.robot.events.subscribe(self._on_robot_state_update, Events.robot_state) - self.robot.events.subscribe(self._on_nav_map_update, Events.nav_map_update) - - @property - def user_data_queue(self): - """A queue to send custom data to the 3D viewer process. - - Best used in conjunction with :func:`~Viewer3DComponent.add_render_call` to place - a process on the 3D viewer process then obtain data from this queue. - """ - return self._user_data_queue - - def add_render_call(self, render_function: callable, *args): - """Allows external functions to be injected into the viewer process which - will be called at the appropriate time in the rendering pipeline. - - Example usage to draw a dot at the world origin: - - .. code-block:: python - - import time - - import anki_vector - - def my_render_function(user_data_queue): - glBegin(GL_POINTS) - glVertex3f(0, 0, 0) - glEnd() - - with anki_vector.Robot(enable_nav_map_feed=True, show_3d_viewer=True) as robot: - robot.viewer_3d.add_render_call(my_render_function) - time.sleep(10) - - :param render_function: The delegated function to be invoked in the pipeline. - :param args: An optional list of arguments to send to the render_function - the arguments list must match the parameters accepted by the - supplied function. - """ - self._extra_render_function_queue.put(_ExternalRenderCallFunctor(render_function, args)) - - def close(self): - """Closes the background process showing the 3D view. - - .. testcode:: - - import anki_vector - - import time - - with anki_vector.Robot(enable_nav_map_feed=True) as robot: - robot.viewer_3d.show() - time.sleep(5) - robot.viewer_3d.close() - """ - if self._close_event: - self._close_event.set() - self._close_event = None - if self._update_thread: - self._update_thread.join(timeout=2) - self._update_thread = None - self._input_intent_queue = None - self._nav_map_queue = None - self._world_frame_queue = None - if self._process: - self._process.join(timeout=5) - if self._process.is_alive(): - self._process.terminate() - self._process = None - - def _update(self): - """Reads most recently stored user-triggered intents, and sends - motor messages to the robot if the intents should effect the robot's - current motion. - - Called on SDK thread, for controlling robot from input intents - pushed from the OpenGL thread. - - :param robot: the robot being updated by this View Controller - """ - close_event = self._close_event - while close_event and not close_event.is_set(): - try: - input_intents = self._input_intent_queue.get(True, timeout=2) # type: RobotControlIntents - - # Track last-used intents so that we only issue motor controls - # if different from the last frame (to minimize it fighting with an SDK - # program controlling the robot): - old_intents = self._last_robot_control_intents - self._last_robot_control_intents = input_intents - - if not old_intents or (old_intents.left_wheel_speed != input_intents.left_wheel_speed - or old_intents.right_wheel_speed != input_intents.right_wheel_speed): - self.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, - _return_future=True) - - if not old_intents or old_intents.lift_speed != input_intents.lift_speed: - self.robot.motors.set_lift_motor(input_intents.lift_speed, _return_future=True) - - if not old_intents or old_intents.head_speed != input_intents.head_speed: - self.robot.motors.set_head_motor(input_intents.head_speed, _return_future=True) - except mp.queues.Empty: - pass - close_event = self._close_event - - def _on_robot_state_update(self, *_): - """Called from SDK process whenever the robot state is updated (so i.e. every engine tick). - - Note: - - This is called from the SDK process, and will pass the nav map data to the - 3D viewer process. - - We can safely capture any robot and world state here, and push to OpenGL - (main) process via a multiprocessing queue. - """ - from .opengl import opengl_vector - world_frame = opengl_vector.WorldRenderFrame(self.robot) - queue = self._world_frame_queue - if queue: - try: - queue.put(world_frame, False) - except mp.queues.Full: - pass - # self._view_controller.update(self.robot) # TODO: <- sounds like this has something to do with keyboard input... - - def _on_nav_map_update(self, _, msg): - """Called from SDK process whenever the nav map is updated. - - Note: - - This is called from the SDK process, and will pass the nav map data to the - 3D viewer process. - - We can safely capture any robot and world state here, and push to OpenGL - (main) process via a multiprocessing queue. - """ - queue = self._nav_map_queue - if queue: - try: - queue.put(msg, False) - except mp.queues.Full: - pass diff --git a/anki_vector/vision.py b/anki_vector/vision.py deleted file mode 100644 index 29172c5..0000000 --- a/anki_vector/vision.py +++ /dev/null @@ -1,167 +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 -# -# https://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. - -"""Utility methods for Vector's vision - -Vector's can detect various types of objects through his camera feed. - -The :class:`VisionComponent` class defined in this module is made available as -:attr:`anki_vector.robot.Robot.vision` and can be used to enable/disable vision -processing on the robot. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['VisionComponent'] - -from concurrent import futures - -from . import util, connection, events -from .messaging import protocol - - -class VisionComponent(util.Component): # pylint: disable=too-few-public-methods - """VisionComponent exposes controls for the robot's internal image processing. - - The :class:`anki_vector.robot.Robot` or :class:`anki_vector.robot.AsyncRobot` instance owns this vision component. - - :param robot: A reference to the owner Robot object. - """ - - def __init__(self, robot): - super().__init__(robot) - - self._detect_faces = False - self._detect_custom_objects = False - # TODO implement - # self._detect_motion = False - self._display_camera_feed_on_face = False - - robot.events.subscribe(self._handle_mirror_mode_disabled_event, events.Events.mirror_mode_disabled) - robot.events.subscribe(self._handle_vision_modes_auto_disabled_event, events.Events.vision_modes_auto_disabled) - - def close(self): - """Close all the running vision modes and wait for a response.""" - vision_mode = self.disable_all_vision_modes() # pylint: disable=assignment-from-no-return - if isinstance(vision_mode, futures.Future): - vision_mode.result() - - def _handle_mirror_mode_disabled_event(self, _, _msg): - self._display_camera_feed_on_face = False - - def _handle_vision_modes_auto_disabled_event(self, _, _msg): - self._detect_faces = False - self._detect_custom_objects = False - # self._detect_motion = False - self._display_camera_feed_on_face = False - - @property - def detect_faces(self): - return self._detect_faces - - @property - def detect_custom_objects(self): - return self._detect_custom_objects - - # TODO implement - # @property - # def detect_motion(self): - # return self._detect_motion - - @property - def display_camera_feed_on_face(self): - return self._display_camera_feed_on_face - - @connection.on_connection_thread() - async def disable_all_vision_modes(self): - if self.detect_faces: - await self.enable_face_detection(False, False) - if self.detect_custom_objects: - await self.enable_custom_object_detection(False) - # if self.detect_motion: - # await self.enable_motion_detection(False) - if self.display_camera_feed_on_face: - await self.enable_display_camera_feed_on_face(False) - - @connection.on_connection_thread() - async def enable_custom_object_detection(self, detect_custom_objects: bool = True): - """Enable custom object detection on the robot's camera - - :param detect_custom_objects: Specify whether we want the robot to detect custom objects. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - robot.vision.enable_custom_object_detection() - """ - self._detect_custom_objects = detect_custom_objects - - enable_marker_detection_request = protocol.EnableMarkerDetectionRequest(enable=detect_custom_objects) - return await self.grpc_interface.EnableMarkerDetection(enable_marker_detection_request) - - @connection.on_connection_thread() - async def enable_face_detection( - self, - detect_faces: bool = True, - # detect_smile: bool = False, - estimate_expression: bool = False, - # detect_blink: bool = False, - # detect_gaze: bool = False - ): - """Enable face detection on the robot's camera - - :param detect_faces: Specify whether we want the robot to detect faces. - :param detect_smile: Specify whether we want the robot to detect smiles in detected faces. - :param estimate_expression: Specify whether we want the robot to estimate what expression detected faces are showing. - :param detect_blink: Specify whether we want the robot to detect how much detected faces are blinking. - :param detect_gaze: Specify whether we want the robot to detect where detected faces are looking. - """ - self._detect_faces = detect_faces - - enable_face_detection_request = protocol.EnableFaceDetectionRequest( - enable=detect_faces, - enable_smile_detection=False, - enable_expression_estimation=estimate_expression, - enable_blink_detection=False, - enable_gaze_detection=False) - return await self.grpc_interface.EnableFaceDetection(enable_face_detection_request) - - # TODO implement - # @connection.on_connection_thread() - # async def enable_motion_detection(self, detect_motion: bool = True): - # """Enable motion detection on the robot's camera - - # :param detect_motion: Specify whether we want the robot to detect motion. - - # .. testcode:: - - # import anki_vector - # with anki_vector.Robot() as robot: - # robot.vision.enable_motion_detection(detect_motion=True) - # """ - # self._detect_motion = detect_motion - - # enable_motion_detection_request = protocol.EnableMotionDetectionRequest(enable=detect_motion) - # return await self.grpc_interface.EnableMotionDetection(enable_motion_detection_request) - - @connection.on_connection_thread() - async def enable_display_camera_feed_on_face(self, display_camera_feed_on_face: bool = True): - """Display the robot's camera feed on its face along with any detections (if enabled) - - :param display_camera_feed_on_face: Specify whether we want to display the robot's camera feed on its face. - """ - self._display_camera_feed_on_face = display_camera_feed_on_face - - display_camera_feed_request = protocol.EnableMirrorModeRequest(enable=display_camera_feed_on_face) - return await self.grpc_interface.EnableMirrorMode(display_camera_feed_request) diff --git a/anki_vector/world.py b/anki_vector/world.py deleted file mode 100644 index 43e6b3a..0000000 --- a/anki_vector/world.py +++ /dev/null @@ -1,818 +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 -# -# https://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's known view of his world. - -This view includes objects and faces it knows about and can currently -see with its camera. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['World'] - -from concurrent import futures -from typing import Iterable - -from . import faces -from . import connection -from . import objects -from . import util - -from .events import Events -from .messaging import protocol - - -class World(util.Component): - """Represents the state of the world, as known to Vector.""" - - #: callable: The factory function that returns a - #: :class:`faces.Face` class or subclass instance - face_factory = faces.Face - - #: callable: The factory function that returns an - #: :class:`objects.LightCube` class or subclass instance. - light_cube_factory = objects.LightCube - - #: callable: The factory function that returns an - #: :class:`objects.Charger` class or subclass instance. - charger_factory = objects.Charger - - #: callable: The factory function that returns an - #: :class:`objects.CustomObject` class or subclass instance. - custom_object_factory = objects.CustomObject - - #: callable: The factory function that returns an - #: :class:`objects.FixedCustomObject` class or subclass instance. - fixed_custom_object_factory = objects.FixedCustomObject - - def __init__(self, robot): - super().__init__(robot) - - self._custom_object_archetypes = {} - - # Objects by type - self._faces = {} - self._light_cube = {objects.LIGHT_CUBE_1_TYPE: self.light_cube_factory(robot=robot)} - self._custom_objects = {} - - #: :class:`anki_vector.objects.Charger`: Vector's charger. - #: ``None`` if no charger connected or known about yet. - self._charger = None # type: anki_vector.objects.Charger - - # All objects - self._objects = {} - - # Subscribe to callbacks that updates the world view - 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, - on_connection_thread=True) - - #### Public Properties #### - - @property - def all_objects(self): - """generator: yields each object in the world. - - .. testcode:: - - # Print the all objects' class details - import anki_vector - with anki_vector.Robot() as robot: - for obj in robot.world.all_objects: - print(obj) - - Returns: - A generator yielding :class:`anki_vector.faces.Face`, :class:`anki_vector.faces.LightCube`, - :class:`anki_vector.faces.Charger`, :class:`anki_vector.faces.CustomObject` and - :class:`anki_vector.faces.FixedCustomObject` instances. - """ - for obj in self._objects.values(): - yield obj - - @property - def visible_faces(self) -> Iterable[faces.Face]: - """generator: yields each face that Vector can currently see. - - .. testcode:: - - # Print the visible face's attributes - import anki_vector - with anki_vector.Robot() as robot: - for face in robot.world.visible_faces: - print("Face attributes:") - print(f"Face id: {face.face_id}") - print(f"Updated face id: {face.updated_face_id}") - print(f"Name: {face.name}") - print(f"Expression: {face.expression}") - print(f"Timestamp: {face.timestamp}") - print(f"Pose: {face.pose}") - print(f"Image Rect: {face.face_rect}") - print(f"Expression score: {face.expression_score}") - print(f"Left eye: {face.left_eye}") - print(f"Right eye: {face.right_eye}") - print(f"Nose: {face.nose}") - print(f"Mouth: {face.mouth}") - - Returns: - A generator yielding :class:`anki_vector.faces.Face` instances. - """ - for face in self._faces.values(): - if face.is_visible: - yield face - - @property - def custom_object_archetypes(self) -> Iterable[objects.CustomObjectArchetype]: - """generator: yields each custom object archetype that Vector will look for. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - for obj in robot.world.custom_object_archetypes: - print(obj) - - Returns: - A generator yielding CustomObjectArchetype instances - """ - for obj in self._custom_object_archetypes.values(): - yield obj - - @property - def visible_custom_objects(self) -> Iterable[objects.CustomObject]: - """generator: yields each custom object that Vector can currently see. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - for obj in robot.world.visible_custom_objects: - print(obj) - - Returns: - A generator yielding CustomObject instances - """ - for obj in self._custom_objects.values(): - if obj.is_visible: - yield obj - - @property - def connected_light_cube(self) -> objects.LightCube: - """A light cube connected to Vector, if any. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - if robot.world.connected_light_cube: - dock_response = robot.behavior.dock_with_cube(robot.world.connected_light_cube) - """ - result = None - cube = self._light_cube.get(objects.LIGHT_CUBE_1_TYPE) - if cube and cube.is_connected: - result = cube - - return result - - @property - def light_cube(self) -> objects.LightCube: - """Returns the vector light cube object, regardless of its connection status. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - cube = robot.world.light_cube - if cube: - if cube.is_connected: - print("LightCube is connected.") - else: - print("LightCube isn't connected") - - Raises: - :class:`ValueError` if the cube_id is invalid. - """ - cube = self._light_cube.get(objects.LIGHT_CUBE_1_TYPE) - # Only return the cube if it has an object_id - if cube.object_id is not None: - return cube - return None - - @property - def charger(self) -> objects.Charger: - """Returns the most recently observed Vector charger object, or None if no chargers have been observed. - - .. testcode:: - - import anki_vector - - # First, place Vector directly in front of his charger so he can observe it. - - with anki_vector.Robot() as robot: - print('most recently observed charger: {0}.'.format(robot.world.charger)) - """ - if self._charger is not None: - return self._charger - return None - - #### Public Methods #### - - def close(self): - """The world will tear down all its faces and objects.""" - - # delete_custom_objects is called before the _objects are torn down to make sure the - # robot receives cues to remove the internal representations of these objects before - # we release the SDK side representations - self.delete_custom_objects() - - 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_object_observed, - Events.robot_observed_object) - - def get_object(self, object_id: int): - """Fetches an object instance with the given id. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - # First get an existing object id, for instance: - valid_object_id = 1 - - # Then use the object_id to retrieve the object instance: - my_obj = robot.world.get_object(valid_object_id) - """ - return self._objects.get(object_id) - - def get_face(self, face_id: int) -> faces.Face: - """Fetches a Face instance with the given id. - - .. testcode:: - - with anki_vector.Robot() as robot: - # First get an existing face_id, for instance: - previously_observed_face_id = 1 - - # Then use the face_id to retrieve the Face instance - my_face = robot.world.get_face(previously_observed_face_id) - """ - return self._faces.get(face_id) - - @connection.on_connection_thread() - async def connect_cube(self) -> protocol.ConnectCubeResponse: - """Attempt to connect to a cube. - - If a cube is currently connected, this will do nothing. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.world.connect_cube() - """ - req = protocol.ConnectCubeRequest() - result = await self.grpc_interface.ConnectCube(req) - - # dispatch cube connected message - event = protocol.ObjectConnectionState( - object_id=result.object_id, - factory_id=result.factory_id, - connected=result.success, - object_type=objects.LIGHT_CUBE_1_TYPE) - - await self._robot.events.dispatch_event(event, Events.object_connection_state) - - return result - - @connection.on_connection_thread() - async def disconnect_cube(self) -> protocol.DisconnectCubeResponse: - """Requests a disconnection from the currently connected cube. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.world.disconnect_cube() - """ - req = protocol.DisconnectCubeRequest() - return await self.grpc_interface.DisconnectCube(req) - - @connection.on_connection_thread() - async def flash_cube_lights(self) -> protocol.FlashCubeLightsResponse: - """Flash cube lights - - Plays the default cube connection animation on the currently - connected cube's lights. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - robot.world.flash_cube_lights() - """ - req = protocol.FlashCubeLightsRequest() - return await self.grpc_interface.FlashCubeLights(req) - - @connection.on_connection_thread(requires_control=False) - async def forget_preferred_cube(self) -> protocol.ForgetPreferredCubeResponse: - """Forget preferred cube. - - 'Forget' the robot's preferred cube. This will cause the robot to - connect to the cube with the highest RSSI (signal strength) next - time a connection is requested. - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - robot.world.forget_preferred_cube() - """ - req = protocol.ForgetPreferredCubeRequest() - return await self.grpc_interface.ForgetPreferredCube(req) - - @connection.on_connection_thread(requires_control=False) - async def set_preferred_cube(self, factory_id: str) -> protocol.SetPreferredCubeResponse: - """Set preferred cube. - - Set the robot's preferred cube and save it to disk. The robot - will always attempt to connect to this cube if it is available. - This is only used in simulation (for now). - - .. testcode:: - - import anki_vector - - with anki_vector.Robot() as robot: - connected_cube = robot.world.connected_light_cube - if connected_cube: - robot.world.set_preferred_cube(connected_cube.factory_id) - - :param factory_id: The unique hardware id of the physical cube. - """ - req = protocol.SetPreferredCubeRequest(factory_id=factory_id) - return await self.grpc_interface.SetPreferredCube(req) - - @connection.on_connection_thread(requires_control=False) - async def delete_custom_objects(self, - delete_custom_marker_objects: bool = True, - delete_fixed_custom_objects: bool = True, - delete_custom_object_archetypes: bool = True): - """Causes the robot to forget about custom objects it currently knows about. - - .. testcode:: - - import anki_vector - with anki_vector.Robot() as robot: - robot.world.delete_custom_objects() - """ - - last_blocking_call = None - - if delete_custom_object_archetypes: - self._custom_object_archetypes.clear() - req = protocol.DeleteCustomObjectsRequest(mode=protocol.CustomObjectDeletionMode.Value("DELETION_MASK_ARCHETYPES")) - last_blocking_call = await self.grpc_interface.DeleteCustomObjects(req) - delete_custom_marker_objects = True - - if delete_custom_marker_objects: - self._remove_all_custom_marker_object_instances() - req = protocol.DeleteCustomObjectsRequest(mode=protocol.CustomObjectDeletionMode.Value("DELETION_MASK_CUSTOM_MARKER_OBJECTS")) - last_blocking_call = await self.grpc_interface.DeleteCustomObjects(req) - - if delete_fixed_custom_objects: - self._remove_all_fixed_custom_object_instances() - req = protocol.DeleteCustomObjectsRequest(mode=protocol.CustomObjectDeletionMode.Value("DELETION_MASK_FIXED_CUSTOM_OBJECTS")) - last_blocking_call = await self.grpc_interface.DeleteCustomObjects(req) - - return last_blocking_call - - @connection.on_connection_thread(requires_control=False) - async def define_custom_box(self, - custom_object_type: objects.CustomObjectTypes, - marker_front: objects.CustomObjectMarkers, - marker_back: objects.CustomObjectMarkers, - marker_top: objects.CustomObjectMarkers, - marker_bottom: objects.CustomObjectMarkers, - marker_left: objects.CustomObjectMarkers, - marker_right: objects.CustomObjectMarkers, - depth_mm: float, - width_mm: float, - height_mm: float, - marker_width_mm: float, - marker_height_mm: float, - is_unique: bool = True) -> objects.CustomObject: - """Defines a cuboid of custom size and binds it to a specific custom object type. - - The robot will now detect the markers associated with this object and send an - object_observed message when they are seen. The markers must be placed in the center - of their respective sides. All 6 markers must be unique. - - :param custom_object_type: the object type you are binding this custom object to. - :param marker_front: the marker affixed to the front of the object. - :param marker_back: the marker affixed to the back of the object. - :param marker_top: the marker affixed to the top of the object. - :param marker_bottom: the marker affixed to the bottom of the object. - :param marker_left: the marker affixed to the left of the object. - :param marker_right: the marker affixed to the right of the object. - :param depth_mm: depth of the object (in millimeters) (X axis). - :param width_mm: width of the object (in millimeters) (Y axis). - :param height_mm: height of the object (in millimeters) (Z axis). - (the height of the object) - :param marker_width_mm: width of the printed marker (in millimeters). - :param maker_height_mm: height of the printed marker (in millimeters). - :param is_unique: If True, the robot will assume there is only 1 of this object. - (and therefore only 1 of each of any of these markers) in the world. - - .. testcode:: - - import anki_vector - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - robot.world.define_custom_box(custom_object_type=anki_vector.objects.CustomObjectTypes.CustomType00, - marker_front= anki_vector.objects.CustomObjectMarkers.Circles2, - marker_back= anki_vector.objects.CustomObjectMarkers.Circles3, - marker_top= anki_vector.objects.CustomObjectMarkers.Circles4, - marker_bottom= anki_vector.objects.CustomObjectMarkers.Circles5, - marker_left= anki_vector.objects.CustomObjectMarkers.Triangles2, - marker_right= anki_vector.objects.CustomObjectMarkers.Triangles3, - depth_mm=20.0, width_mm=20.0, height_mm=20.0, - marker_width_mm=50.0, marker_height_mm=50.0) - - Returns: - CustomObject instance with the specified dimensions. - This is None if the definition failed internally. - Note: No instances of this object are added to the world until they have been seen. - - Raises: - TypeError if the custom_object_type is of the wrong type. - ValueError if the 6 markers aren't unique. - """ - if not isinstance(custom_object_type, objects._CustomObjectType): # pylint: disable=protected-access - raise TypeError("Unsupported object_type, requires CustomObjectType") - - # verify all 6 markers are unique - markers = {marker_front, marker_back, marker_top, marker_bottom, marker_left, marker_right} - if len(markers) != 6: - raise ValueError("all markers must be unique for a custom box") - - custom_object_archetype = objects.CustomObjectArchetype(custom_object_type, - depth_mm, width_mm, height_mm, - marker_width_mm, marker_height_mm, - is_unique) - - definition = protocol.CustomBoxDefinition(marker_front=marker_front.id, - marker_back=marker_back.id, - marker_top=marker_top.id, - marker_bottom=marker_bottom.id, - marker_left=marker_left.id, - marker_right=marker_right.id, - x_size_mm=depth_mm, - y_size_mm=width_mm, - z_size_mm=height_mm, - marker_width_mm=marker_width_mm, - marker_height_mm=marker_height_mm) - - req = protocol.DefineCustomObjectRequest(custom_type=custom_object_type.id, - is_unique=is_unique, - custom_box=definition) - - response = await self.grpc_interface.DefineCustomObject(req) - - if response.success: - type_id = custom_object_archetype.custom_type.id - self._custom_object_archetypes[type_id] = custom_object_archetype - return custom_object_archetype - - self.logger.error("Failed to define Custom Object %s", custom_object_archetype) - return None - - @connection.on_connection_thread(requires_control=False) - async def define_custom_cube(self, - custom_object_type: objects.CustomObjectTypes, - marker: objects.CustomObjectMarkers, - size_mm: float, - marker_width_mm: float, - marker_height_mm: float, - is_unique: bool = True) -> objects.CustomObject: - """Defines a cube of custom size and binds it to a specific custom object type. - - The robot will now detect the markers associated with this object and send an - object_observed message when they are seen. The markers must be placed in the center - of their respective sides. - - :param custom_object_type: the object type you are binding this custom object to. - :param marker: the marker affixed to every side of the cube. - :param size_mm: size of each side of the cube (in millimeters). - :param marker_width_mm: width of the printed marker (in millimeters). - :param maker_height_mm: height of the printed marker (in millimeters). - :param is_unique: If True, the robot will assume there is only 1 of this object - (and therefore only 1 of each of any of these markers) in the world. - - .. testcode:: - - import anki_vector - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - robot.world.define_custom_cube(custom_object_type=anki_vector.objects.CustomObjectTypes.CustomType00, - marker=anki_vector.objects.CustomObjectMarkers.Circles2, - size_mm=20.0, - marker_width_mm=50.0, marker_height_mm=50.0) - - Returns: - CustomObject instance with the specified dimensions. - This is None if the definition failed internally. - Note: No instances of this object are added to the world until they have been seen. - - Raises: - TypeError if the custom_object_type is of the wrong type. - """ - - if not isinstance(custom_object_type, objects._CustomObjectType): # pylint: disable=protected-access - raise TypeError("Unsupported object_type, requires CustomObjectType") - - custom_object_archetype = objects.CustomObjectArchetype(custom_object_type, - size_mm, size_mm, size_mm, - marker_width_mm, marker_height_mm, - is_unique) - - definition = protocol.CustomCubeDefinition(marker=marker.id, - size_mm=size_mm, - marker_width_mm=marker_width_mm, - marker_height_mm=marker_height_mm) - - req = protocol.DefineCustomObjectRequest(custom_type=custom_object_type.id, - is_unique=is_unique, - custom_cube=definition) - - response = await self.grpc_interface.DefineCustomObject(req) - - if response.success: - type_id = custom_object_archetype.custom_type.id - self._custom_object_archetypes[type_id] = custom_object_archetype - return custom_object_archetype - - self.logger.error("Failed to define Custom Object %s", custom_object_archetype) - return None - - @connection.on_connection_thread(requires_control=False) - async def define_custom_wall(self, - custom_object_type: objects.CustomObjectTypes, - marker: objects.CustomObjectMarkers, - width_mm: float, - height_mm: float, - marker_width_mm: float, - marker_height_mm: float, - is_unique: bool = True) -> objects.CustomObject: - """Defines a wall of custom width and height, with a fixed depth of 10mm, and binds it to a specific custom object type. - - The robot will now detect the markers associated with this object and send an - object_observed message when they are seen. The markers must be placed in the center - of their respective sides. - - :param custom_object_type: the object type you are binding this custom object to. - :param marker: the marker affixed to the front and back of the wall. - :param width_mm: width of the object (in millimeters). (Y axis). - :param height_mm: height of the object (in millimeters). (Z axis). - :param width_mm: width of the wall (along Y axis) (in millimeters). - :param height_mm: height of the wall (along Z axis) (in millimeters). - :param marker_width_mm: width of the printed marker (in millimeters). - :param maker_height_mm: height of the printed marker (in millimeters). - :param is_unique: If True, the robot will assume there is only 1 of this object - (and therefore only 1 of each of any of these markers) in the world. - - .. testcode:: - - import anki_vector - with anki_vector.Robot(enable_custom_object_detection=True) as robot: - robot.world.define_custom_wall(custom_object_type=anki_vector.objects.CustomObjectTypes.CustomType00, - marker=anki_vector.objects.CustomObjectMarkers.Circles2, - width_mm=20.0, height_mm=20.0, - marker_width_mm=50.0, marker_height_mm=50.0) - - Returns: - CustomObject instance with the specified dimensions. - This is None if the definition failed internally. - Note: No instances of this object are added to the world until they have been seen. - - Raises: - TypeError if the custom_object_type is of the wrong type. - """ - - if not isinstance(custom_object_type, objects._CustomObjectType): # pylint: disable=protected-access - raise TypeError("Unsupported object_type, requires CustomObjectType") - - thickness_mm = protocol.ObjectConstants.Value("FIXED_CUSTOM_WALL_THICKNESS_MM") - - custom_object_archetype = objects.CustomObjectArchetype(custom_object_type, - thickness_mm, width_mm, height_mm, - marker_width_mm, marker_height_mm, - is_unique) - - definition = protocol.CustomWallDefinition(marker=marker.id, - width_mm=width_mm, - height_mm=height_mm, - marker_width_mm=marker_width_mm, - marker_height_mm=marker_height_mm) - - req = protocol.DefineCustomObjectRequest(custom_type=custom_object_type.id, - is_unique=is_unique, - custom_wall=definition) - - response = await self.grpc_interface.DefineCustomObject(req) - - if response.success: - type_id = custom_object_archetype.custom_type.id - self._custom_object_archetypes[type_id] = custom_object_archetype - return custom_object_archetype - - self.logger.error("Failed to define Custom Object %s", custom_object_archetype) - return None - - def create_custom_fixed_object(self, - pose: util.Pose, - x_size_mm: float, - y_size_mm: float, - z_size_mm: float, - relative_to_robot: bool = False, - use_robot_origin: bool = True) -> objects.FixedCustomObject: - """Defines a cuboid of custom size and places it in the world. It cannot be observed. - - :param pose: The pose of the object we are creating. - :param x_size_mm: size of the object (in millimeters) in the x axis. - :param y_size_mm: size of the object (in millimeters) in the y axis. - :param z_size_mm: size of the object (in millimeters) in the z axis. - :param relative_to_robot: whether or not the pose given assumes the robot's pose as its origin. - :param use_robot_origin: whether or not to override the origin_id in the given pose to be - the origin_id of Vector. - - .. testcode:: - - import anki_vector - from anki_vector.util import degrees, Pose - - with anki_vector.Robot() as robot: - robot.world.create_custom_fixed_object(Pose(100, 0, 0, angle_z=degrees(0)), - x_size_mm=10, y_size_mm=100, z_size_mm=100, - relative_to_robot=True) - - Returns: - FixedCustomObject instance with the specified dimensions and pose. - """ - # Override the origin of the pose to be the same as the robot's. This will make sure they are in - # the same space in the robot every time. - if use_robot_origin: - pose = util.Pose(x=pose.position.x, y=pose.position.y, z=pose.position.z, - q0=pose.rotation.q0, q1=pose.rotation.q1, - q2=pose.rotation.q2, q3=pose.rotation.q3, - origin_id=self._robot.pose.origin_id) - - # In this case define the given pose to be with respect to the robot's pose as its origin. - if relative_to_robot: - pose = self._robot.pose.define_pose_relative_this(pose) - - response = self._create_custom_fixed_object(pose, x_size_mm, y_size_mm, z_size_mm) - if isinstance(response, futures.Future): - response = response.result() - - fixed_custom_object = self.fixed_custom_object_factory( - self._robot, - pose, - x_size_mm, - y_size_mm, - z_size_mm, - response.object_id) - - if fixed_custom_object: - self._objects[fixed_custom_object.object_id] = fixed_custom_object - return fixed_custom_object - - @connection.on_connection_thread(requires_control=False) - async def _create_custom_fixed_object(self, - pose: util.Pose, - x_size_mm: float, - y_size_mm: float, - z_size_mm: float): - """Send the CreateFixedCustomObject rpc call on the connection thread.""" - req = protocol.CreateFixedCustomObjectRequest( - pose=pose.to_proto_pose_struct(), - x_size_mm=x_size_mm, - y_size_mm=y_size_mm, - z_size_mm=z_size_mm) - - return await self.grpc_interface.CreateFixedCustomObject(req) - - #### Private Methods #### - - def _allocate_custom_marker_object(self, msg): - # obj is the base object type for this custom object. We make instances of this for every - # unique object_id we see of this custom object type. - first_custom_type = protocol.ObjectType.Value("FIRST_CUSTOM_OBJECT_TYPE") - if msg.object_type < first_custom_type or msg.object_type >= first_custom_type + protocol.CustomType.Value("CUSTOM_TYPE_COUNT"): - self.logger.error('Received a custom object observation with a type not inside the custom object range: %s. Msg=%s', - msg.object_type, msg) - return None - - # Object observation events contain an object_type. A subset of that object_type enum maps to the - # custom_type enum, so we perform the conversion. - custom_type = msg.object_type - first_custom_type + objects.CustomObjectTypes.CustomType00.id - archetype = self._custom_object_archetypes.get(custom_type) - if not archetype: - self.logger.error('Received a custom object type: %s that has not been defined yet. Msg=%s', - msg.object_type, msg) - return None - - custom_object = self.custom_object_factory(self._robot, - archetype, - msg.object_id) - - self.logger.debug('Allocated object_id=%s to CustomObject %s', msg.object_id, custom_object) - - if custom_object: - self._custom_objects[msg.object_id] = custom_object - return custom_object - - def _allocate_charger(self, msg): - charger = self.charger_factory(self._robot, msg.object_id) - if self._charger: - self.logger.warning('Allocating multiple chargers: existing charger=%s msg=%s', self._charger, msg) - return None - - self._charger = charger - - self.logger.debug('Allocated object_id=%s to Charger %s', msg.object_id, charger) - return charger - - def _remove_all_custom_marker_object_instances(self): - for obj_id, obj in list(self._custom_objects.items()): - if isinstance(obj, objects.CustomObject): - self.logger.info("Removing CustomObject instance: id %s = obj '%s'", obj_id, obj) - self._custom_objects.pop(obj_id, None) - - def _remove_all_fixed_custom_object_instances(self): - for obj_id, obj in list(self._custom_objects.items()): - if isinstance(obj, objects.FixedCustomObject): - self.logger.info("Removing FixedCustomObject instance: id %s = obj '%s'", obj_id, obj) - self._custom_objects.pop(obj_id, None) - - #### Private Event Handlers #### - - def _on_face_observed(self, _, msg): - """Adds a newly observed face to the world view.""" - if msg.face_id not in self._faces or msg.face_id not in self._objects: - pose = util.Pose(x=msg.pose.x, y=msg.pose.y, z=msg.pose.z, - q0=msg.pose.q0, q1=msg.pose.q1, - q2=msg.pose.q2, q3=msg.pose.q3, - origin_id=msg.pose.origin_id) - image_rect = util.ImageRect(msg.img_rect.x_top_left, - msg.img_rect.y_top_left, - msg.img_rect.width, - msg.img_rect.height) - face = self.face_factory(self.robot, - pose, image_rect, msg.face_id, msg.name, msg.expression, msg.expression_values, - msg.left_eye, msg.right_eye, msg.nose, msg.mouth, msg.timestamp) - if face: - self._faces[face.face_id] = face - self._objects[face.face_id] = face - - def _on_object_observed(self, _, msg): - """Adds a newly observed custom object to the world view.""" - if msg.object_family == protocol.ObjectFamily.Value("LIGHT_CUBE"): - if msg.object_id not in self._objects: - if self.light_cube: - self._objects[msg.object_id] = self.light_cube - - if msg.object_family == protocol.ObjectFamily.Value("CHARGER"): - if msg.object_id not in self._objects: - charger = self._allocate_charger(msg) - if charger: - self._objects[msg.object_id] = charger - - if msg.object_family == protocol.ObjectFamily.Value("CUSTOM_OBJECT"): - if msg.object_id not in self._objects: - custom_object = self._allocate_custom_marker_object(msg) - if custom_object: - self._objects[msg.object_id] = custom_object diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 701c5ae..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,229 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -ifeq ($(VERBOSE),1) - VERBOSE_OPTS = -vv -endif -SPHINXOPTS = $(VERBOSE_OPTS) -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " epub3 to make an epub3" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" - @echo " dummy to check syntax errors of document sources" - -.PHONY: clean -clean: - rm -rf $(BUILDDIR)/* - rm -rf source/generated - -.PHONY: html -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -.PHONY: dirhtml -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -.PHONY: singlehtml -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -.PHONY: pickle -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -.PHONY: json -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -.PHONY: htmlhelp -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -.PHONY: qthelp -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CozmoSDK.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CozmoSDK.qhc" - -.PHONY: applehelp -applehelp: - $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp - @echo - @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." - @echo "N.B. You won't be able to view it unless you put it in" \ - "~/Library/Documentation/Help or install it in your application" \ - "bundle." - -.PHONY: devhelp -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/CozmoSDK" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/CozmoSDK" - @echo "# devhelp" - -.PHONY: epub -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -.PHONY: epub3 -epub3: - $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 - @echo - @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." - -.PHONY: latex -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -.PHONY: latexpdf -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -.PHONY: latexpdfja -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -.PHONY: text -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -.PHONY: man -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -.PHONY: texinfo -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -.PHONY: info -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -.PHONY: gettext -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -.PHONY: changes -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -.PHONY: linkcheck -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -.PHONY: doctest -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -.PHONY: coverage -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -.PHONY: xml -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -.PHONY: pseudoxml -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." - -.PHONY: dummy -dummy: - $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy - @echo - @echo "Build finished. Dummy builder generates no files." diff --git a/docs/ext/__init__.py b/docs/ext/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/docs/ext/custopoleon/__init__.py b/docs/ext/custopoleon/__init__.py deleted file mode 100644 index 72a4120..0000000 --- a/docs/ext/custopoleon/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Due to deficiencies in the design of Sphinx, two modules cannot both -observe the same event. Napaleon hooks the autodoc-skip-member event -to determine which members to show.. We want to use the same thing -to special case the AnimTriggers and BehaviorTypes classes so that -their undocumented members show up in the docs. - -As a workaround this module monkey patches Napoleon to wrap their -autodoc-skip-member implementation and special case the response for -our classes. -""" - - -import sphinx.ext.napoleon - - -_org_skip_member = sphinx.ext.napoleon._skip_member - -# TODO: determine if this is still necessary -def _skip_member(app, what, name, obj, skip, options): - clsname = obj.__class__.__name__ - if clsname in ('_AnimTrigger', '_BehaviorType'): - return False - if name in ('GatewayWrapper', 'FindInitializationErrors', 'IsInitialized', 'SetInParent', 'WhichOneof'): - return True - return _org_skip_member(app, what, name, obj, skip, options) - -sphinx.ext.napoleon._skip_member = _skip_member - -def setup(app): - return sphinx.ext.napoleon.setup(app) - diff --git a/docs/ext/verlink.py b/docs/ext/verlink.py deleted file mode 100644 index 7fe5b1f..0000000 --- a/docs/ext/verlink.py +++ /dev/null @@ -1,72 +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 -# -# https://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. - -"""Generate versioned links for example files. - -This extension adds a new role to generate links to the correct version -of files referenced by the documentation. - -It substitutes the string "0.0.0" with a version number defined by the -"verlink_version" configuration value. - -Roles can optionally include text to use for the link, else it defaults -to the supplied filename. the URL is prefixed with the -"verlink_base_url" value to make a complete URL. - -For example, if verlink_base_url="https://example.com/files/0.0.0/" -and verlink_version="1.2.3" then - -:verlink:`examples-0.0.0.zip` will display "examples-1.2.3.zip" and link -to https:/example.com/files/1.2.3/examples-1.2.3.zip - -:verlink:`Examples for 0.0.0 ` will display -"Examples for 1.2.3" and link to https:/example.com/files/1.2.3/examples-1.2.3.zip -""" - - -from docutils import nodes, utils - -import sphinx -from sphinx.util.nodes import split_explicit_title - - -def replace_version(app, str): - try: - ver = app.config.verlink_version - except AttributeError as err: - raise ValueError("verlink_version configuration value is not set") - return str.replace('0.0.0', ver) - - -def verlink_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): - app = inliner.document.settings.env.app - try: - base_url = app.config.verlink_base_url - except AttributeError as err: - raise ValueError("verlink_base_url configuration value is not set") - - text = utils.unescape(text) - has_explicit_title, title, fn = split_explicit_title(text) - full_url = replace_version(app, base_url + fn) - if not has_explicit_title: - title = fn - title = replace_version(app, title) - pnode = nodes.reference(title, title, internal=False, refuri=full_url) - return [pnode], [] - - -def setup(app): - app.add_role('verlink', verlink_role) - app.add_config_value('verlink_base_url', None, {}) - app.add_config_value('verlink_version', None, {}) diff --git a/docs/source/.gitignore b/docs/source/.gitignore deleted file mode 100644 index 9ab870d..0000000 --- a/docs/source/.gitignore +++ /dev/null @@ -1 +0,0 @@ -generated/ diff --git a/docs/source/_static/.gitignore b/docs/source/_static/.gitignore deleted file mode 100644 index d2b263c..0000000 --- a/docs/source/_static/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# No files should be checked into this directory -* -!.gitignore diff --git a/docs/source/_templates/layout.html b/docs/source/_templates/layout.html deleted file mode 100644 index 7d57882..0000000 --- a/docs/source/_templates/layout.html +++ /dev/null @@ -1,94 +0,0 @@ -{% extends "!layout.html" %} -{% block extrahead %} - - - - {{ super() }} -{% endblock %} diff --git a/docs/source/advanced-tips.rst b/docs/source/advanced-tips.rst deleted file mode 100644 index fa9c196..0000000 --- a/docs/source/advanced-tips.rst +++ /dev/null @@ -1,74 +0,0 @@ -.. _advanced-tips: - -############# -Advanced Tips -############# - -.. _moving_between_wifi: - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -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, 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 -m anki_vector.configure -u "" -s "" - - -^^^^^^^^^^^^^^^^^^^^^^ -Using multiple Vectors -^^^^^^^^^^^^^^^^^^^^^^ - -If your device is configured to use more than one robot, you can specify -which robot you want to use by passing its serial number as a parameter -to the Robot constructor:: - - - with anki_vector.Robot("00e20142") as robot: - robot.anim.play_animation('anim_pounce_success_02') - - -Alternatively, you can pass a ``--serial`` flag on the command -line, and ``anki_vector.util.parse_command_args`` will parse out -the serial number:: - - ./01_hello_world.py --serial 00e20142 - - -^^^^^^^^^^^^^^^^^^^^^ -Set ANKI_ROBOT_SERIAL -^^^^^^^^^^^^^^^^^^^^^ - -In order to avoid entering Vector's serial number for each program run, -you can create environment variable ``ANKI_ROBOT_SERIAL`` -and set it to Vector's serial number:: - - export ANKI_ROBOT_SERIAL=00e20100 - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Set ANKI_ROBOT_HOST and VECTOR_ROBOT_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 ``anki_vector.configure`` will automatically pick -up those settings:: - - export ANKI_ROBOT_HOST="192.168.42.42" - export VECTOR_ROBOT_NAME=Vector-A1B2 - - - ----- - -`Terms and Conditions `_ and `Privacy Policy `_ - -`Click here to return to the Anki Developer website. `_ diff --git a/docs/source/api.rst b/docs/source/api.rst deleted file mode 100644 index eb2c643..0000000 --- a/docs/source/api.rst +++ /dev/null @@ -1,46 +0,0 @@ -The API -======= - - -.. autosummary:: - :nosignatures: - :toctree: generated - - anki_vector - anki_vector.animation - anki_vector.audio - anki_vector.behavior - anki_vector.camera - anki_vector.color - anki_vector.connection - anki_vector.events - anki_vector.exceptions - anki_vector.faces - anki_vector.lights - anki_vector.motors - anki_vector.nav_map - anki_vector.objects - anki_vector.opengl - anki_vector.opengl.opengl - anki_vector.opengl.opengl_vector - anki_vector.opengl.opengl_viewer - anki_vector.photos - anki_vector.proximity - anki_vector.robot - anki_vector.screen - anki_vector.status - anki_vector.touch - anki_vector.util - anki_vector.viewer - anki_vector.vision - anki_vector.world - - anki_vector.messaging.client - -.. - ----- - -`Terms and Conditions `_ and `Privacy Policy `_ - -`Click here to return to the Anki Developer website. `_ diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index d2c7dab..0000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,405 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# Vector SDK documentation build configuration file, created by -# sphinx-quickstart on Tue Jul 19 17:16:49 2016. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import subprocess -import sys - -sys.path.extend([os.path.abspath('../..'), os.path.abspath('..')]) -here = os.path.abspath(os.path.dirname(__file__)) -sdk_base_dir = os.path.join(here, '..', '..') - - -def fetch_sdk_version(): - with open(os.path.join(sdk_base_dir, 'anki_vector', 'version.py')) as f: - ns = {} - exec(f.read(), ns) - return ns - -version_data = fetch_sdk_version() -sdk_version = version_data['__version__'] - - -version = sdk_version - -if '.dev' in version: - try: - git_branch = subprocess.check_output( - ('git', 'symbolic-ref', '--short', 'HEAD'), - universal_newlines=True) - - git_commit = subprocess.check_output( - ('git', 'rev-parse', 'HEAD'), - universal_newlines=True) - - version = "%s - %s (%s)" % (git_branch, git_commit[:8], sdk_version) - - except subprocess.CalledProcessError as e: - print("Failed to run git: %s" % e) - - - - - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx_autodoc_typehints', - 'sphinx.ext.autosummary', - 'sphinx.ext.intersphinx', - 'sphinx.ext.doctest', # Test sample code snippets in the docs - 'ext.verlink', # build versioned links for example packages - 'ext.custopoleon' # Customizes the Napoleon package - #'sphinx.ext.napoleon' -] - -verlink_base_url = 'https://sdk-resources.anki.com/vector/%s/' % sdk_version -verlink_version = sdk_version - -intersphinx_mapping = { - 'python': ('https://docs.python.org/3.6', None), - 'numpy': ('https://docs.scipy.org/doc/numpy/', None), - 'PIL': ('https://pillow.readthedocs.io/en/latest/', None) - } - -autosummary_generate = True -autodoc_default_flags = ['members'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The encoding of source files. -# -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = 'Vector SDK' -copyright = '2018, Anki' -author = 'Anki' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -#version = '0.1' - -# The full version, including alpha/beta/rc tags. -release = '0.1' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# -# today = '' -# -# Else, today_fmt is used as the format for a strftime call. -# -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -#html_theme = 'alabaster' -#html_theme = 'default' -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = [] - -# The name for this set of Sphinx documents. -# " v documentation" by default. -# -# html_title = 'Vector SDK v0.1' - -# A shorter title for the navigation bar. Default is the same as html_title. -# -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# -# html_logo = None - -# The name of an image file (relative to this directory) to use as a favicon of -# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# -# html_extra_path = [] - -# If not None, a 'Last updated on:' timestamp is inserted at every page -# bottom, using the given strftime format. -# The empty string is equivalent to '%b %d, %Y'. -# -html_last_updated_fmt = '%b, %d, %Y %I:%M:%S %Z' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# -# html_sidebars = {} -#html_sidebars = { -# '**': ['globaltoc.html', 'sourcelink.html', 'searchbox.html'] -#} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# -# html_additional_pages = {} - -# If false, no module index is generated. -# -# html_domain_indices = True - -# If false, no index is generated. -# -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' -# -# html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# 'ja' uses this config value. -# 'zh' user can custom change `jieba` dictionary path. -# -# html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -# -# html_search_scorer = 'scorer.js' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'VectorSDKdoc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'VectorSDK.tex', 'Vector SDK Documentation', - 'Anki', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# -# latex_use_parts = False - -# If true, show page references after internal links. -# -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# -# latex_appendices = [] - -# It false, will not define \strong, \code, itleref, \crossref ... but only -# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added -# packages. -# -# latex_keep_old_macro_names = True - -# If false, no module index is generated. -# -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'vectorsdk', 'Vector SDK Documentation', - [author], 1) -] - -# If true, show URL addresses after external links. -# -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'VectorSDK', 'Vector SDK Documentation', - author, 'VectorSDK', 'The SDK for directly controlling your Vector', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# -# texinfo_appendices = [] - -# If false, no module index is generated. -# -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# -# texinfo_no_detailmenu = False - - -# Customized stuff appears below here diff --git a/docs/source/downloads.rst b/docs/source/downloads.rst deleted file mode 100644 index 6cf7a60..0000000 --- a/docs/source/downloads.rst +++ /dev/null @@ -1,25 +0,0 @@ -######### -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 deleted file mode 100644 index dd47874..0000000 --- a/docs/source/getstarted.rst +++ /dev/null @@ -1,92 +0,0 @@ -=================================== -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 Developer Forums ---------------------- - -Please visit our `Anki Developer Forums `_ where you can: - -* Get assistance with your code - -* Hear about upcoming SDK additions - -* Share your work - -* Join discussion about the SDK - -* Be a part of the Vector SDK Community! - - -------------- -Prerequisites -------------- - -* You have completed the Installation steps, found here: :ref:`initial` -* 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. - -------------------- -Starting Up the SDK -------------------- - -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 ----------------- - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -First Steps - "Hello, World!" -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Let's test your new setup by running a very simple program. This program instructs Vector to say "Hello, World!" - a perfect way to introduce him and you to the world of programming. - -""""""""""" -The Program -""""""""""" - -1. Run the program using the same Terminal (macOS/Linux) / Command Prompt (Windows) window mentioned above. First, change to the ``tutorials`` sub-directory:: - - cd tutorials - -Then, run the program. - - a. For macOS and Ubuntu 18.04 systems, type the following and press **Enter**:: - - ./01_hello_world.py - - The same can also be achieved with:: - - python3 01_hello_world.py - - b. For Windows systems, type the following and press **Enter**:: - - 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. - -.. note:: If you have more than one Vector configured on your device, you can pass the serial number of the Vector you want to use at the command line: - ``./01_hello_world.py --serial {robot_serial_number}`` - - -You are now all set up to run Python programs on Vector. - - - -Now that you have run your own Vector program, take a look at the rest of the Vector SDK and at the many other example programs to get more ideas. - -`Terms and Conditions `_ and `Privacy Policy `_ - -`Click here to return to the Anki Developer website. `_ diff --git a/docs/source/images/custom_markers/LICENSE.txt b/docs/source/images/custom_markers/LICENSE.txt deleted file mode 100644 index fd011d5..0000000 --- a/docs/source/images/custom_markers/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -Anki, Inc. Image License Agreement Version 1.1 (last updated November 15, 2018) - -This Image License Agreement (this "Agreement") governs the terms and conditions of your access to and use of Licensed Materials (as defined below), and is made between you, as an individual or entity ("you"), and Anki, Inc. ("we," "us" or "Licensor"). You accept and agree to be bound by this Agreement by your access to or use of any of the Licensed Materials. - -The "Licensed Materials" are the digital images that we make available to you from time to time in connection with this Agreement. - -1. License. Subject to the terms and conditions of this Agreement, we hereby grant you a limited, revocable, worldwide, fully-paid, royalty free, non-exclusive, non-transferable copyright license during the term of this Agreement to access, copy, display, perform, modify the size of, and distribute, in any of the Licensed Materials, in each case: (A) solely in connection with your use of the Vector SDK in accordance with our separate SDK license agreement(s) or the applicable Anki hardware products (e.g. Vector) and/or the Vector App, and (B) only provided that you comply with the Anki Terms of Use at www.anki.com/terms and any other terms that may apply to the Vector device and/or Vector mobile application and that we may from time to time modify. Licensee may not sublicense any of the foregoing rights, except for the right to access, copy, display perform and distribute the Licensed Materials only in connection with the SDK in an app created by the Licensee. For clarity, this license does not include the right to commercially distribute the Licensed Materials in print form. - -2. Reservation. Licensor (or its suppliers) owns and retains all right, title, and interest in and to each of the Licensed Materials worldwide including, but not limited to, ownership of all copyrights and other intellectual property rights therein. We reserve all rights not explicitly licensed in this Agreement. - -3. DISCLAIMER OF WARRANTY AND LIMITATION OF LIABILITY. THE LICENSED MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NONINFRINGEMENT. IN NO EVENT WILL LICENSOR BE LIABLE TO YOU OR TO ANY THIRD PARTY FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, PUNITIVE OR CONSEQUENTIAL DAMAGES, OR LOST REVENUE, SAVINGS OR PROFITS, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE) OR OTHERWISE, ARISING FROM OR IN CONNECTION WITH ANY OF THE LICENSED MATERIALS, WHETHER OR NOT THAT PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -4. Indemnification. You will defend, indemnify and hold harmless Licensor and its officers, directors, shareholders, employees, and agents from any loss, liability, cost or expense, including attorneys' fees ("Liabilities") that arises from any claim, action or proceeding in connection with your use of the Licensed Materials or your breach of this Agreement. You shall have control of the defense and all related settlement negotiations for such claim, action or proceeding; provided that we shall have the right to consent to any settlement or entry of judgment, such consent not to be unreasonably withheld, and we may participate in such defense using our own counsel at our own expense. - -5. Termination. You may terminate this Agreement at any time by deleting or destroying all copies of the Licensed Materials that you possess or control. We may terminate this Agreement and/or your license to any or all of the Licensed Materials at any time without prior notice to you. In case of termination, you must cease all access and use of, and delete or destroy, all copies of the Licensed Materials that you possess or control. Sections 2 through 8 of this Agreement will survive termination of this Agreement. - -6. Modifications to this Agreement and Licensed Materials. We may amend this Agreement at any time by posting an amended version online and/or sending information regarding the amendment to your email address of record with us. You shall be deemed to have accepted such amendments by continuing to access and/or use any Licensed Materials after such amendments have been posted or information regarding such amendments has been sent to you. If you do not agree to any of such changes, you may terminate this Agreement and immediately cease all access to and use of Licensed Materials. You agree that such termination will be your exclusive remedy in such event. No other waiver or modification of this Agreement shall be valid unless in writing and signed by both parties. We also reserve the right at any time and from time to time to modify or discontinue all or any portion of any Licensed Materials without notice to you. We shall not be liable to you or any third party should we exercise such rights. - -7. Assignment. You may not assign this Agreement, in whole or in part, without our prior written consent, and any attempt by you to assign this Agreement without such consent shall be void. Subject to the foregoing, this Agreement shall benefit and bind both parties, and their successors and permitted assigns. - -8. General. You shall comply with all laws, rules and regulations applicable to your activities under this Agreement. This Agreement shall be governed by and construed in accordance with the laws of the State of California, U.S.A., except for its conflicts of laws principles. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. This Agreement will not be construed to create or imply any partnership, agency or joint venture between the parties. If any provision of this Agreement is found illegal or unenforceable, it will be enforced to the maximum extent permissible, and the legality and enforceability of the other provisions of this Agreement will not be affected. This Agreement is the complete agreement between the parties with respect to its subject matter, and supersedes any prior agreements and communications (both written and oral) regarding such subject matter. diff --git a/docs/source/images/custom_markers/SDK_2Circles.png b/docs/source/images/custom_markers/SDK_2Circles.png deleted file mode 100644 index 5dd31085eed5f79783c156eea75af80ffdf869e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13336 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 MyLBIGA35>A05zRGBLDyZ diff --git a/docs/source/images/custom_markers/SDK_2Diamonds.png b/docs/source/images/custom_markers/SDK_2Diamonds.png deleted file mode 100644 index 93d45f6d79c53997f6c6618d0788cd4dec2481a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14337 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 z 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 UUewCpK)%yDqpO~$YIW^D06h96S^xk5 diff --git a/docs/source/images/custom_markers/SDK_2Triangles.png b/docs/source/images/custom_markers/SDK_2Triangles.png deleted file mode 100644 index acc57d4f219ee2764070382355619fc0b24508b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12597 zcmch7c{r8rxBuQGlBqXQC{v-#vu&Px&0~f#W>##rdEQDWNyt1#MafjA%#kEQhLl;x z$UM(yJ@vlt@0{=TJ?Hve=a17>vY&lF_pt7@KI^lFyV{zU$w}!*Q79C-%Abn5C=>x6 z`A0$kPi_~Qxx;^F+?9;n^{}??UY0l;l$!5Gs zZls|uZiRK?u|&r3cssek+bEQzjJJ!W)eReWrt3C#4$e|6i&b?jOb*skEQZ1wd>Sqa zHuesG`r>T#d^Pp0d~aA`tXX8FnIyf%VFD)`cS|O3r<=}h;@(m$zvdN(-;u|>EKI+K zxZjXs`MoJ44Q(a`EY5~Wm`8-$ieE^WNfg7wFCr*}!EiAN@bQcB@`>^C3v%;|hzs$F z3kWd%^@jyk!&%#k>nbY$wHADmVzGC3cM<31_44xK@e<_0;_P_&F&GRlp8&6b05^=_ zcJpy|xAf+Ac4Pf}21Oe;E1ZLiy93sl37OIII@ZHoiUoH1`v^`h8XEsh?CkcJpg?52 z-j*)B{5*WTPEN?VevNi>*R}b7+xWMm-SmB2YDI z;#xQdxD?BqidZWTCmUyX6-6l)_=d;Y!CG9{%34gsQjnk9%EnrhTgXPpnj2#+%Ev9n zCnCUaYiTPcin08Ao`3f~zlf-qu%MiPk^rAFKfjWgyt16IvY-GzMod{$Sxij+?{`(4 z-P|pmt!(~Y+X2@8>s|i;@m+BRoQq7BZ$)5cmEhjn85C2Mhqf3uJOKNWfT*!tKFg7cO;Neitdde;W}Ut~NeGq{|S|Dfzksq=i# zi>IXq&kgeQziig_mUs8vJNg0F{$^y6(O;VhGrcLfxwqivw%{hS!T-z3|IdHmEyC|+ z77JmAt2;)hPn|kNL*x0Q>uF{t6DzBNfb71{Tz+}F-@=>ejGzP2y6sQAfy)uij6ssi zH@*j{Dx)tOOP{X(YS1W2V&;`Iw7mPmxer^VY0&ZPp#`4`A!|3#t};_t*P~vha=UGBQC^ zZAos^?HXJ@PthUmV~t^pwYv-0fVpcU+wERve)Gb?9HW`7ZEdOiR>}?zKaB0tPDR|M zC8@bhOgZe@eAaxcf%cEq@2r;}kSL;wHOrlc;(1KPgAVp+xUc?67@i)myK%vBOE6=* zO^vtaJUI~qNn;pg-Bj|8XPRk_EyopyA1j2_?(`Nee0x3J!k+Ocl)zai`*IWyFE1ni z^$-rcwHrvsxxeH`^yPCW?hrU@D}<7LE4FHeeOw@AN(xv_85yi_9q-nX;U*6ddy6YlqRI5)dcSE&||BMu_LuzC~rem)sOYT zglcMPBZ0f~8Han*8_Pum1y)wg5B7iB$qbag%~yLpiuPZ^z&kzqt?XKg*`H4-o^bd$(mRrvwK_Tu5eVw$+eA4IbAQadx* zv?eaWDX*m!?~y!U7Z&ztjpyDR_AA^V64Tk=S>N662nuXEWf3Q$(D0Rubw`_^*EQOgNH@ zwM+9bfFlGg-DV7zM<0%mvtg^|%iP*Am#I~9Rj`geb{<{ees4P5ja zk=XtDTIxKCPF+Ps#nN(&yL>PUAFJ={yHj4bKY#2bn>QaysHiqzx6Elkq?p2>zQt>5 zc}qidNXTPWpOO%-%x_VTfBEud)k38jnms`)a3ywix?>ZCvR4Gv3L1|TJ?A}&CQ9Wh zcr%q;dZRCS2NmhOx3#P#x%^JBLo)Uzd;0xrI_-)4;{ziHo?N!{ns`|;m(luMo$PbI zGt5K`HZ{KMzDsY3K~RqG36C_xR#a70&3q=~CNPr|{r3de*{7>U_6s(MNRl6%l?VhC zyx$xmZlk$~Z`M`R2+JH?|_Tm1)X5C_UPRGIiE^E*;m1V)vo}M0L)_z*1t(DbP znawvH7aEh;l+cA&OJ4*X9y}A4|b6h*W4utF`aHt17OnS z9PP9psoi($kV;5Oa#$b;8Cne7ta2WzJS*l>PbzlApwKKo0G$O$K@n|oLp@%{_mI)(qya*(RmBfb6^T zu|hun_KlB^E`msXL&?r`m1E6`7)%mOJ(C* zA0J`;$LV2Xt6-)Zv%Q=>IW9AukBLD;7pfKmx5mOUd}p77oXpki)5DjNd ziZWsqOJj|ZXFEBbX=NOMNr<=`wx>8(wtOY}6FfAnxhU)=MjeKo#*wy$smf$mXr%@Nwkfju@H!A6m}U1PxEU7Kg(q&XTS?T?a=hs2<6+bi}p+!+A<~1CBn}9+OJ>dOEI3BQ827nx=UC+pLE{b@y zKHYcrV)yZIyj{AFG70Gx;l$L`(bf9u12V-%#P!3JY}nfEXHXoaC0T~As-HWBRYGp!TC+c zUvpMbS)MscMCLJHIakoy(NXx~g{p=|+ODro8bec$ z-l{xHe5DsxF5tec`>cuNxuCVrmq%#Vf%!#5MBM2vG_M2xRm+=txD|EykSxc3du8%% zb=y_6LZfksy$%@C;hQ*MJ$Ddgj%XU*lwsFOmvrblKZYj=jqL+uURhb8+{*@z2E0Tn zsWAx@7<{YrQq+qVFS@$A+8DL#U1CITetnpb;8iUH-pWf^SsBL`8y9y)N5{2VW*#80 z>#5o&WSoHS+D!4yZ?7n~kKeg-$HvB{j_K>`Tie+MO_+0nXXws}dIJjR z2*2mU?&5=dfIQtkO0Jsj5=u_L0p2jnfO7rOBk-unns5hY2eswbxi2_QWsF3g{B#+s z3YV%ee|WIF!H1^iV=x5D00(oCKePt!vpV%jE!5?Y)_pYL(bP_v?PgO+G!a?z6*IH+ z*jTr$Lq~9b8p%R;Jq2Lz1Hdxw9^~D+bt{h3Fxe2c-MBW_R|vRt{=hOY@E}?EM%LMO z@DG6aet#VDMXLkIwDEw5OmbGb2VcE<1%9KYp<9ckos(H83lLCR)Lo`_lf{rmM*qw<++a*|hCj z%i}qkX}B$MnvXDOWxd32eNlI$I7H7CvKqlL?pl`sZ`h!vx zEUp+D_BE5+fMy_!^pli!{rSSw)YRCo*NWrWG*i7HA`2Bw_04bT_x~c0iNE_2$YlE9 z@V(MUMn^I}PMwP6U}eoVGv`_I>wP43%s?UJP+~_OFUtEl_ANerINdfA31L&&WGj#_% z_EBu?Ymd1vE_YtTFfdt>deN(|tXlwqEJbb+uo?IC!hVprBqk=NC`8b{Gjn@&TC{ER zUi0{g3PT2JzMe0q3{7+l{W!lUbJ3`+1#n1*F}Xez78WM=$tB;=J(7ucu!gM-mN@7U z26acXUT#m9lGqwE0TxGo_&C^a1-eh=_>Ou;Av)#bE%goq-rK?RueC$|JYLI63ol%*OL0KYsk!*4CyhiI3o| z^{&UGZ7*_IWU;ZbT7gbS2A+o4^{lkNcW1*HDc?1if^PHl_2RF?HGT@Nhz5zFISpQN zaB-0}M4xwLyKq4^>Xx_U@WaJ>2UI{OH6-Qu4du`$&yv3eosN6-$OPh`x5w;2$3WiX zAKoIEYiE~I4A&oibO5dvf|B^y*pnfw4#U;kps}}N5kmydATCHl&8Nc$Hvo2UWcFcy ziNkB4uuKdsfFxJTozLdFfRpWg<7T%C1IIQNhVP*zizZtWyym{-yGA{>>qv)4>iAUw zu+ZR|-&Y4i-j*MxR!7VI*J_XfG;M5bi}MTREvF6MrlZuHxt58^NM5uY>Jr ztyHn%;@aJbmRNR;Wa_Ujj*gB59=`<2(J)shbkM~T$8sA*_K zz9YA*uU}%_5)*n@YiQ(HTZ+5J4cJLHgqS6MNyPm6`Lf zy4CypNsx>P2uc8)y3c?Sa39mZa^*sN!;}bz1%cD}`$#$Drab5SuM~Pk5^Z;f%a}x+ zlY+d2qw{S7whG^kpJ#?SJs|YFcZzd%aSn;2^l~)E0?guoZ-&Bxyj-y4|iWzwRXD&}9woW_>x1laurHt5@=8+ku+ztk2n{ z!z@N!-QC&CfpHN$mG zL?3C5`0uEoy>VNK)Dry@+S3pqRyuS)`;e*$ng(>ykllRtdjzlaYMUTyth$ZO3ekCL(82Zz>9@(r z$&aXMfp1WqfByV=bRDPDC`38a9im?{@yxRET*h4x5`M^-g8ill->|KaLn|1zzwwk2k5Et zS)C$3Cuu|xR1UCy|2`Q8mh$a+q7s`X;q`Hh(PO^f11J>^sO-A^Z_1EVlJ5su%)x~fOu_5f|D+i#YQim zJ!4zWvfMUmvtq5qU^a0=~4m+JvI? zGlbSR3Tq60J-zw0=rL>um;3Zeo&iJ|AK+A}F3iz=<3jp2Gne zk5$dS$OB)=78Yv&q$nkYlbD*%g#ba$zb>gfYOt6O@O~;DIKb4z#D_6zQ%Jg6r_Y`l7#g<1Q2!DmuJ{=;#!K3Uf2Bc@3H(F8y|#;D!01wV za4d(gadT@>a9E610FAiB$vLTAUg@ybQ~knv?!77Hh*8pZmPl9Cehx1U2Pu6@m%4^5E|p+{gA~|Z20DD-woZM$AH?+FUf9AyKLfqJQSe3? z8D$0TVU;2&)P2aK&*SG;9eDPqBP0s0)%w3-pwk7G2l?Ph(6J|Fz;&w5i&P)ysj&E3 zWC?pwh)EW6{k}b&!5gNmqo)@e^SvvCAanxZYyg;H?*$-}27CdLnu=XZ%mu)AoV}t1 zgw$VNCNXH&KUBP0>R9T~&4v*wfSnaU+6Gyk1+gGtY7BHkE0e8SGW+frF#3zsw6qEF z@gJE<0QVrfbOA2&{2jmvZ%Ljoe4@C=EU-yrqv0w1R__z}ttO~AdV6~TJ&>JR+t}JJ z4A)$vkn0BE0LwFhmBL>FBv6A2GUFkmG5E?x4O!?GWWj*Fa)wzrf}i&Bc>_D8l0$ag z_2e!C!5omkxuL3ZgMimvW z&!-rnTbZdn7l+fLqnY7Zh#3+zU`Keuie@061kPdtcHepJwXH^AU@e2ZHJ}*$jRoD1 ze_2#i^zx-T#aLH~L-$z;Pvp*-%Wm}X$Y_&ggNP~Vg#LuJNuI*d+YMWzVQWkJ#q)K z5{G-M?COb3#9A*v`i&}DS|c@nPnGXS1f@dJ1cI95G7;kfHwgQ5Bhf7y?>$1aS6- z2H&^=2S60AC;RS6b|iO+8w~)rpAz$3K8zrACmQ5rYRXM~@GS|V6-cWga#(MeE?#B+ z%{*C=cZlBdOM4%Hr!y_rRr_f?#q519sMotG$aEwp<`J zS>?4Dk8D(3LnG49>Wey(_b`GAND@>$QsPDzf_H)$Lz1{ z#IL81;^Gj`5Pb3#xG12th+x^D1EIe0`-|WH6j@2L3t<344lKl)4K@j;;MS4r zPA!6n)5QI=5^i+j-MA}#LKc23r0IHbaxxvp<oQQ}LZVtI<5 zUoQ=mA-KOq0-4Pf1&P@&;L!(lK~*Tn%dX6}K-6GWoCv_Ngx1y5t62~C74Tjf1&5BD zk1-Q49+G>w%ygjJ-R~VCimihx7i1@tD0(vX_WZ9!WJ3rIl(+{$D(%AGqysUx&9&Ly z8vwAl%v!)Xh$oTmW`n;!O97Lo2I(%hxYb@+}wwRa-HCq^9m|eL98(i;5Y}0tuAAzr~o0b-OnOH zmWn2jLVy%LH}Nf2c6K|&jAYR#U4hR)q=RG9rr|L$($!^w%Ze6u?8yZ;-5Q(;+W_C- z+3~8HlTij!8+GlcptDDCyR8 z3u7Wp%r&kH3;!ieQ9A!7|W zO@O$>PSDpbh@d8A!+#D{iC$>3IZ33Rc!8!?dO5Q5;>C+^w!TLockm8EivHJjS{N#E zAT=E=EkF|zwLiAD4pg{iGmvD1CLom#kc|BE>S{^I<57h4fXA!?$^$!Q?gr=5)uoG} zGW*9e6CZ*!0*TANlH(1~Luz(ygmZ4C~;2qh989Lg7k}w1eForC{J7X1NLIgWMy?mI+B?l{uv_gqML; zg69T5rjCb@97%EDm{!0(Q+``za3^p{#B3juL2mw4z^*5OTj?4sq z1l&HJu;rqR%t+ZZhXq|Tz{ekFxrk#7Ae&)n`4~ zAn)(X6%2pk1Z~?+3)Q~+Q<4(|my5MHxt zBe=KOA)pMEp7WQ8x2gb|0V)uINICWu3_%ut5_Omh1e;#uCNSAVU#Rw$K`hdO9=7}T z$_61~vS>ZtqhzODemQ{yXaXqb=7fPKBAUQo+#vK7OGlzHuVEi#Wb+iTK=&DKOnul_ z;JHy#ifFwA2(yG_TQj88hqGwgXl_PaV41)YXBj!G5hcRSQI6-cmo&7Q_ z^MI3=7mF|%d}S}_#`;`eD>LTlJ$(Pn*J21*=_gSh3!`sugXv$6I(G_q7Uj~1kq_w1 z0`FsV|E{p`UrG-DyDG!~P}BHb?f7qM8h zATz4KRQn1F6sZ z=o7w$P{^}eLv#|VV0&VWen0td_inwvf5wfA%%H%;p$L!PZL^a;d5_Z(5=i6*=Y;=t(o`ZWdZ_wXcuXg|5d9u zlhZLa_TiGOrXfF~DtYcOA!s{+%2Wwp=w{EY>p#C1H_K1bk8FkRW(70Z1NhWz4$~K? z*@IUmGtq`_j=Jrsl(A%4=pcI8u}whN2V?}wev>Yyl0nN6=BFzx-axhR!JSTMF}b2O zi4X0|(aMlmtQNeV@X55w^GvOUagoK_Vi$3sN=V}ffB?N>CYp>aY+@ujgX}I0Z6pQ%7`#q)2!bM4clDFOs``6zS^Gs}GJ%O4{BXIh3aBSV4JbG@tW( z2?2aGChZYNH6`GMMM&53|VPm*U-as?x-Y(TA$}7YJ(9jq&ys6ESMQo@t9??Q>t&G|cf2#ld?3c4m9+_WB)fB8eKL4BBeEhOOx( zJ4*5#qWIi0>^sByV{qdEv(IyYl76znyKIj$9)1M^C_3!T`hxv*)W%i~)SQ9BKnYPK z4&7k0{Jn`%YE3&ti;rfTS)D70F>uQ=14l{4;x*{n3=}In;6btV=?@SM3~1bQZ#kG{3|k+V=eG^$r6Uda-=h6pJoJL`gi z1nHSRuY2k^fvG+k#Y1bCRdGfkdlCYyL^0hA@tuI>@GqB;2R&x z&I+Vsi0q8O1SEN_UZr`f;vQ^5v?c|C8u;ZTXjhui3fk!g>BJTV#FpbvLg&>mT|r{54Al!(*~0lYHyCfJqGF0)7$+;@EzNsPCYbx&Nz(XJ7TK~3|l z*B}z9Xh5r@l$0qGihVlH&@2hEO?|Z+!a93`GK2?wKrHJ(XdRNP>N=>@+;^%|F={+M z&?}^5N#x(Ku{d%FO%xZ4NzRGfOiU9;IsSl68bc5bT>TI%^Up@otw&^ZLNm~NArCg9 zgkBmun5#R;gX-+BzPp$Bxq75!sUzrM6>{pTTu5=8h+wJ149Z^+93hF6;kuwYA!?U2 z3{-B_`=V{JE*J5w2K|T4w-h~SnbQ+N? zzpHDp5Po7gn{nSB3acZB2b)Td@$|y)jBY}i5waO{qRy#8coGQ^rdRE18bqxb0FhnV z_2@1z9f#sS_E+}J$OV&5rKl4>gvX9X(gfL+@Q*U^r9e{6X+{M;AGB}h(Q?0AyhyZ zL~a^EgTn6Gk8aqx_n;?-EUOr}B|X4rXpxeFF1y41k;62OAu#(0B{X{k&PF sXB_&#{Rr{=pLZ7jUXXEDhJXyUPw1}q+4DFwLZVPAN}7rV@)o!L13{i%9smFU diff --git a/docs/source/images/custom_markers/SDK_3Circles.png b/docs/source/images/custom_markers/SDK_3Circles.png deleted file mode 100644 index c138c11ddc1b2f2fb8a281f9a21a0e14f7f79d22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14771 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 diff --git a/docs/source/images/custom_markers/SDK_3Hexagons.png b/docs/source/images/custom_markers/SDK_3Hexagons.png deleted file mode 100644 index b002d34beef0a76f696bdb0e1ca13421ca10b242..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12156 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 diff --git a/docs/source/images/custom_markers/SDK_3Triangles.png b/docs/source/images/custom_markers/SDK_3Triangles.png deleted file mode 100644 index 901b855160c7360cf100ed3e02caacf13f9d8e69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13360 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 bKAjoT%9=N|=`5c`_)1w(P2shiS>S&F!l9HS diff --git a/docs/source/images/custom_markers/SDK_4Circles.png b/docs/source/images/custom_markers/SDK_4Circles.png deleted file mode 100644 index 75408ac02ace462ed3079adf4fd8f7270bda246b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13949 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 E9|SRP-T(jq diff --git a/docs/source/images/custom_markers/SDK_4Diamonds.png b/docs/source/images/custom_markers/SDK_4Diamonds.png deleted file mode 100644 index 0ec84717572aff7aa83725d09b8ded60398fe0d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17717 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 MUPJE9E%S%}3vrlGEC2ui diff --git a/docs/source/images/custom_markers/SDK_4Hexagons.png b/docs/source/images/custom_markers/SDK_4Hexagons.png deleted file mode 100644 index b80002e0d5f9f7322f8c50740c6e6727ae296f73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13045 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 B;CKK4 diff --git a/docs/source/images/custom_markers/SDK_5Circles.png b/docs/source/images/custom_markers/SDK_5Circles.png deleted file mode 100644 index 39a465773b8d0ee2189686b0b3fb809cf61e3f86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17357 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 WxWe*~(DfeTGd-RC+V?aaPW&$bP!+uZ diff --git a/docs/source/images/custom_markers/SDK_5Diamonds.png b/docs/source/images/custom_markers/SDK_5Diamonds.png deleted file mode 100644 index e033799e9ad92f36dfcf6f3ec6fe49103e2a9193..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19192 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 b+xZDOWE842=ULW><5yPDIGrtLcJ+S&io3rl diff --git a/docs/source/images/custom_markers/SDK_5Hexagons.png b/docs/source/images/custom_markers/SDK_5Hexagons.png deleted file mode 100644 index 00fc45a18a164850ba5a2fda434e0cbdebe87648..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15618 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 diff --git a/docs/source/images/vector-sdk-alpha.jpg b/docs/source/images/vector-sdk-alpha.jpg deleted file mode 100644 index 91c538977c7f8fd34ce66fd4f2b51f642aec61cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47066 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 O95sL9SDuf#C;tLBN#`_ for more details. - -.. toctree:: - :maxdepth: 1 - :caption: Installation - - initial - install-macos.rst - install-windows.rst - install-linux.rst - troubleshooting.rst - advanced-tips.rst - -.. toctree:: - :maxdepth: 2 - :caption: Downloads - - downloads.rst - -.. toctree:: - :maxdepth: 2 - :caption: Get Started - - getstarted.rst - -.. toctree:: - :maxdepth: 2 - :caption: API Reference - - api - ------------------- -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - ----- - -`Terms and Conditions `_ and `Privacy Policy `_ - -`Click here to return to the Anki Developer website. `_ diff --git a/docs/source/initial.rst b/docs/source/initial.rst deleted file mode 100644 index 089d859..0000000 --- a/docs/source/initial.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. _initial: - -############# -Initial Setup -############# - ------------- -Installation ------------- - -To install the SDK on your system, select the instructions for your computer's operating system. - -* :ref:`install-macos` -* :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: - -^^^^^^^^^^^^^^^^^^^^^ -Anki Developer Forums -^^^^^^^^^^^^^^^^^^^^^ - -Please visit the `Anki Developer Forums `_ to ask questions, find solutions and for general discussion. - ----- - -`Terms and Conditions `_ and `Privacy Policy `_ - -`Click here to return to the Anki Developer website. `_ diff --git a/docs/source/install-linux.rst b/docs/source/install-linux.rst deleted file mode 100644 index e5c48a9..0000000 --- a/docs/source/install-linux.rst +++ /dev/null @@ -1,133 +0,0 @@ -.. _install-linux: - -#################### -Installation - Linux -#################### - -^^^^^^^^^^^^^ -Prerequisites -^^^^^^^^^^^^^ - -* 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. -* You can see Vector's eyes on his screen. - - -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 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 18.04 -^^^^^^^^^^^^ - -""""""""""""""""""" -Python Installation -""""""""""""""""""" - -1. Type the following into your Terminal window to install Python:: - - sudo apt-get update - sudo apt-get install python3 - -2. Then install pip by typing in the following into the Terminal window:: - - sudo apt install python3-pip - -"""""""""""""""" -SDK Installation -"""""""""""""""" - -To install the SDK, type the following into the Terminal window:: - - python3 -m pip install --user anki_vector - -^^^^^^^^^^^^^^^^^^^^^ -Vector Authentication -^^^^^^^^^^^^^^^^^^^^^ - -To authenticate with the robot, type the following into the Terminal window:: - - 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:: By running the ``anki_vector.configure`` executable submodule, you will be asked to provide your Anki account credentials, and the script will automatically download an authentication token and certificate to your computer 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). - - The downloaded access token is equivalent to your account credentials. It will be stored in your user directory (~/.anki_vector) along with a robot identity certificate and other useful data for establishing a connection. Do not share your access token. - -.. 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:: - - python3.6 -m pip install --user anki_vector - -^^^^^^^^^^^^^^^^^^^^^ -Vector Authentication -^^^^^^^^^^^^^^^^^^^^^ - -To authenticate with the robot, type the following into the Terminal window:: - - 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:: By running the ``anki_vector.configure`` executable submodule, you will be asked to provide your Anki account credentials, and the script will automatically download an authentication token and certificate to your computer 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). - - The downloaded access token is equivalent to your account credentials. It will be stored in your user directory (~/.anki_vector) along with a robot identity certificate and other useful data for establishing a connection. Do not share your access token. - -.. warning:: These credentials give full access to your robot, including camera stream, audio stream and data. Do not share these credentials. - - -^^^^^^^^^^^^^^^ -Troubleshooting -^^^^^^^^^^^^^^^ - -Please see the :doc:`Troubleshooting ` page for tips, or visit the `Anki Developer Forums `_ to ask questions, find solutions, or for general discussion. - ----- - -`Terms and Conditions `_ and `Privacy Policy `_ - -`Click here to return to the Anki Developer website. `_ diff --git a/docs/source/install-macos.rst b/docs/source/install-macos.rst deleted file mode 100644 index e229cca..0000000 --- a/docs/source/install-macos.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. _install-macos: - -########################### -Installation - macOS / OS X -########################### - -This guide provides instructions on installing the Vector SDK for computers running with a macOS operating system. - -^^^^^^^^^^^^^ -Prerequisites -^^^^^^^^^^^^^ - -* 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. -* You can see Vector's eyes on his screen. - - -^^^^^^^^^^^^^^^^^^^ -Python Installation -^^^^^^^^^^^^^^^^^^^ - -1. Install `Homebrew `_ on your system according to the latest instructions. If you already had brew installed then update it by opening a Terminal window and typing in the following:: - - brew update - -2. Once Homebrew is installed and updated, type the following into the Terminal window to install the latest version of Python 3:: - - brew install python3 - -The Vector SDK supports Python 3.6.1 or later. - - -^^^^^^^^^^^^^^^^ -SDK Installation -^^^^^^^^^^^^^^^^ - -To install the SDK, navigate into the SDK folder and type the following into a Terminal window:: - - python3 -m pip install --user anki_vector - -^^^^^^^^^^^^^^^^^^^^^ -Vector Authentication -^^^^^^^^^^^^^^^^^^^^^ - -To authenticate with the robot, type the following into the Terminal window:: - - 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:: By running the ``anki_vector.configure`` executable submodule, you will be asked to provide your Anki account credentials, and the script will automatically download an authentication token and certificate to your computer 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). - - The downloaded access token is equivalent to your account credentials. It will be stored in your user directory (~/.anki_vector) along with a robot identity certificate and other useful data for establishing a connection. Do not share your access token. - -.. warning:: These credentials give full access to your robot, including camera stream, audio stream and data. Do not share these credentials. - -^^^^^^^^^^^^^^^ -Troubleshooting -^^^^^^^^^^^^^^^ - -Please see the :doc:`Troubleshooting ` page for tips, or visit the `Anki Developer Forums `_ to ask questions, find solutions, or for general discussion. - ----- - -`Terms and Conditions `_ and `Privacy Policy `_ - -`Click here to return to the Anki Developer website. `_ diff --git a/docs/source/install-windows.rst b/docs/source/install-windows.rst deleted file mode 100644 index fd750ae..0000000 --- a/docs/source/install-windows.rst +++ /dev/null @@ -1,70 +0,0 @@ -.. _install-windows: - -###################### -Installation - Windows -###################### - -This guide provides instructions on installing the Vector SDK for computers running with a Windows operating system. - -^^^^^^^^^^^^^ -Prerequisites -^^^^^^^^^^^^^ - -* 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. -* You can see Vector's eyes on his screen. - - -^^^^^^^^^^^^^^^^^^^ -Python Installation -^^^^^^^^^^^^^^^^^^^ - -Download the `Python 3.6.1 (or later) executable file from Python.org `_ and -run it on your computer. - -.. important:: Be sure to tick the "Add Python 3.6 to PATH" checkbox on the Setup screen. Then tap "Install Now" and complete the Python installation. - -^^^^^^^^^^^^^^^^ -SDK Installation -^^^^^^^^^^^^^^^^ - -To install the SDK, navigate into the SDK folder and type the following into a Command Prompt window:: - - 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`` - -.. note:: If you encounter an error during SDK installation, you may need to upgrade your Python Setuptools. Try ``py -3 -m pip install --upgrade setuptools`` - -^^^^^^^^^^^^^^^^^^^^^ -Vector Authentication -^^^^^^^^^^^^^^^^^^^^^ - -To authenticate with the robot, type the following into the Terminal window:: - - 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:: By running the ``anki_vector.configure`` executable submodule, you will be asked to provide your Anki account credentials, and the script will automatically download an authentication token and certificate to your computer 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). - - The downloaded access token is equivalent to your account credentials. It will be stored in your user directory (~/.anki_vector) along with a robot identity certificate and other useful data for establishing a connection. Do not share your access token. - -.. warning:: These credentials give full access to your robot, including camera stream, audio stream and data. Do not share these credentials. - -^^^^^^^^^^^^^^^ -Troubleshooting -^^^^^^^^^^^^^^^ - -Please see the :doc:`Troubleshooting ` page for tips, or visit the `Anki Developer Forums `_ to ask questions, find solutions, or for general discussion. - ----- - -`Terms and Conditions `_ and `Privacy Policy `_ - -`Click here to return to the Anki Developer website. `_ diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst deleted file mode 100644 index 863af48..0000000 --- a/docs/source/troubleshooting.rst +++ /dev/null @@ -1,94 +0,0 @@ -.. _troubleshooting: - -############### -Troubleshooting -############### - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Failure to Install Python Package -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you run into trouble installing Python packages, please upgrade your pip install as follows: - - On macOS and Linux:: - - pip3 install -U pip - - On Windows:: - - python -m pip install --upgrade pip - - Alternatively on Windows, try:: - - py -3 -m pip install --upgrade pip - - You may also need to update Python setuptools (for instance, for error ``AttributeError: '_NamespacePath' object has no attribute 'sort'``):: - - pip install --upgrade setuptools - - After applying these updates, retry your Python package installation. - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -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 ``anki_vector.configure``. If so, see :ref:`moving_between_wifi` to set up the robot with the new IP address. - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Failure to run anki_vector.configure -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The prerequisites to run the ``anki_vector.configure`` executable submodule successfully are: - -* 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. - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Vector behaves unexpectedly -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You may need to reboot your robot when you are finished running programs with the pre-alpha Vector SDK. - - -^^^^^^^^^^^^^^^^^^^^^ -Can't find robot name -^^^^^^^^^^^^^^^^^^^^^ - -Your Vector robot name looks like "Vector-E5S6". Find your robot name by placing Vector on the charger and double-clicking Vector's backpack button. - - -^^^^^^^^^^^^^^^^^^^^^^^^ -Can't find serial number -^^^^^^^^^^^^^^^^^^^^^^^^ - -Your Vector's serial number looks like "00e20142". Find your robot serial number on the underside of Vector. Or, find the serial number from Vector's debug screen: double-click his backpack, move his arms up and down, then look for "ESN" on his screen. - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -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 Developer Forums -^^^^^^^^^^^^^^^^^^^^^ - -Please visit the `Anki Developer Forums `_ to ask questions, find solutions and for general discussion. - ----- - -`Terms and Conditions `_ and `Privacy Policy `_ - -`Click here to return to the Anki Developer website. `_ diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 195e77a..0000000 --- a/examples/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# 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/examples/apps/3d_viewer/3d_viewer.py b/examples/apps/3d_viewer/3d_viewer.py deleted file mode 100755 index 125158b..0000000 --- a/examples/apps/3d_viewer/3d_viewer.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""3d Viewer example, with remote control. - -This is an example of how you can use the 3D viewer with a program, and the -3D Viewer and controls will work automatically. -""" - -import time - -import anki_vector - - -def main(): - args = anki_vector.util.parse_command_args() - with anki_vector.Robot(args.serial, - show_viewer=True, - enable_camera_feed=True, - show_3d_viewer=True, - enable_face_detection=True, - enable_custom_object_detection=True, - enable_nav_map_feed=True): - print("Starting 3D Viewer. Use Ctrl+C to quit.") - try: - while True: - time.sleep(0.5) - except KeyboardInterrupt: - pass - - -if __name__ == "__main__": - main() diff --git a/examples/apps/interactive_shell/interactive_shell.py b/examples/apps/interactive_shell/interactive_shell.py deleted file mode 100755 index a9d3cbb..0000000 --- a/examples/apps/interactive_shell/interactive_shell.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Command Line Interface for Vector - -This is an example of integrating Vector with an ipython-based command line interface. -""" - -import sys - -try: - from IPython.terminal.embed import InteractiveShellEmbed -except ImportError: - sys.exit('Cannot import from ipython: Do `pip3 install ipython` to install') - -import anki_vector - -usage = """Use the [tab] key to auto-complete commands, and see all available methods and properties. - -For example, type 'robot.' then press the [tab] key and you'll see all the robot functions. -Keep pressing tab to cycle through all of the available options. - -All IPython commands work as usual. -Here's some useful syntax: - robot? -> Details about 'robot'. - robot?? -> More detailed information including code for 'robot'. -These commands will work on all objects inside of the shell. - -You can even call the functions that send messages to Vector, and he'll respond just like he would in a script. -Try it out! Type: - robot.anim.play_animation('anim_pounce_success_02') -""" - -args = anki_vector.util.parse_command_args() - -ipyshell = InteractiveShellEmbed(banner1='\nWelcome to the Vector Interactive Shell!', - exit_msg='Goodbye\n') - -if __name__ == "__main__": - with anki_vector.Robot(args.serial, - enable_camera_feed=True, - show_viewer=True) as robot: - # Invoke the ipython shell while connected to Vector - ipyshell(usage) diff --git a/examples/apps/proximity_mapper/lib/proximity_mapper_state.py b/examples/apps/proximity_mapper/lib/proximity_mapper_state.py deleted file mode 100755 index 5bcd1c1..0000000 --- a/examples/apps/proximity_mapper/lib/proximity_mapper_state.py +++ /dev/null @@ -1,354 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Contains the state of an environment being explored by the robot. -This is a support class for the proximity_mapper example. - -Includes a utility for rendering the state in OpenGL. -""" - -# __all__ should order by constants, event classes, other classes, functions. -__all__ = ['ClearedTerritory', 'MapState', 'Wall', 'WallSegment'] - -from math import cos, sin, pi -import multiprocessing as mp -from typing import List - -from anki_vector.opengl import opengl -from anki_vector.util import Vector3 - -try: - from OpenGL.GL import (GL_AMBIENT, GL_BLEND, GL_DIFFUSE, GL_FILL, GL_FRONT, GL_FRONT_AND_BACK, GL_LIGHTING, - GL_LINES, GL_ONE_MINUS_SRC_ALPHA, GL_POINTS, GL_QUADS, GL_SHININESS, GL_SPECULAR, GL_SRC_ALPHA, - glBegin, glBlendFunc, glColor, glDisable, glEnable, glEnd, glMaterialfv, glNormal3f, - glPointSize, glPolygonMode, glVertex3f) - -except ImportError as import_exc: - opengl.raise_opengl_or_pillow_import_error(import_exc) - - -# Constants - -#: Visually represent not just walls, but open/loose nodes, territories and navigation -#: points in the 3D Viewer. -RENDER_METADATA_OBJECTS = True - -#: Display color of potential exploration targets in the 3D Viewer. -NODE_OPEN_RENDER_COLOR = [0.0, 1.0, 1.0, 0.75] -#: Display size (in pixels) of potential exploration targets in the 3D Viewer. -NODE_OPEN_RENDER_SIZE = 5.0 - -#: Display color of detected proximity collisions in the 3D Viewer. -#: These nodes will be collected into walls, and will only live long term if isolated -#: too far from any other contact points -NODE_CONTACT_RENDER_COLOR = [1.0, 0.0, 0.0, 0.75] -#: Display size (in pixels) of detected proximity collisions in the 3D Viewer. -NODE_CONTACT_RENDER_SIZE = 2.0 - -#: Display color of the rendered disc for the position the robot will next navigate to. -NAV_POINT_RENDER_COLOR = [0.0, 1.0, 0.0, 1.0] -#: Sections of the rendered disc for the position the robot will next navigate to. -NAV_POINT_RENDER_SECTIONS = 16 -#: Display size of the rendered disc for the position the robot will next navigate to. -NAV_POINT_RENDER_SIZE = 25.0 - -#: Display color of the rendered disc for the territories the robot has already explored. -TERRITORY_RENDER_COLOR = [0.15, 0.6, 1.0, 1.0] -#: Sections of the rendered disc for the territories the robot has already explored. -TERRITORY_RENDER_SECTIONS = 32 - -#: Display color for the walls the robot has identified in the environment. -WALL_RENDER_COLOR = [1.0, 0.4, 0.1, 1.0] -#: Render height of the walls the robot has identified in the environment. -#: This values is purely cosmetic. As the proximity sensor is at a static height and -#: always faces forward, the robot has no way of detecting through this method how tall the -#: obstacles are, so the 100mm height was tuned to be similar to Vector in the viewer -#: rather than reflecting the objects in the environment. -WALL_RENDER_HEIGHT_MM = 100.0 - - -class WallSegment: - """Two points defining a segment of wall in the world - - :param a: The first end of the wall segment expected to be in the xy plane - :param b: The second end of the wall segment expected to be in the xy plane - """ - - def __init__(self, a: Vector3, b: Vector3): - self._a = a - self._b = b - - # precalculate the normal for use in the render call - aToB = b - a - facing_vector = aToB.cross(Vector3(0, 0, 1)) - self._normal = facing_vector.normalized - - @property - def a(self) -> Vector3: - """:return: The first end of the wall segment.""" - return self._a - - @property - def b(self) -> Vector3: - """:return: The second end of the wall segment.""" - return self._b - - @property - def normal(self) -> Vector3: - """:return: The precalculated normal of the wall segment.""" - return self._normal - - -class Wall: - """A chain of WallSegments making up a continuous wall in 3d space - - :param segment: The initial wall segment for this wall - """ - - def __init__(self, segment: WallSegment): - self._vertices = [segment.a, segment.b] - - def insert_head(self, vertex: Vector3): - """Adds a new vertex to the front of the wall - - :param vertex: The coordinates of the vertex being added to the front of the wall. - This point is expected to be near the current head, and in the same xy plane. - """ - self._vertices.insert(0, vertex) - - def insert_tail(self, vertex: Vector3): - """Adds a new vertex to the end of the wall - - :param vertex: The coordinates of the vertex being added to the end of the wall. - This point is expected to be near the current tail, and in the same xy plane. - """ - self._vertices.append(vertex) - - @property - def vertices(self) -> List[Vector3]: - """:return: All the vertices defining the ground vertices of the wall.""" - return self._vertices - - @property - def segments(self) -> List[WallSegment]: - """:return: Constructs and returns the WallSegments which make up this wall.""" - result: List[WallSegment] = [] - for i in range(len(self._vertices) - 1): - result.append(WallSegment(self._vertices[i], self._vertices[i + 1])) - return result - - -class ClearedTerritory: - """A zone of space that the robot has already explored. These are used to - prevent the robot from populating new open nodes in finished areas. - - Cleared Territories always exist in the x-y plane. - - :param center: Centerpoint of the zone - :param radius: The size of the zone - """ - - def __init__(self, center: Vector3, radius: Vector3): - self._center = center - self._radius = radius - - @property - def center(self) -> Vector3: - """:return: The centerpoint of the territory.""" - return self._center - - @property - def radius(self) -> float: - """:return: The radius of the territory.""" - return self._radius - - -class MapState: - """A collection of walls, nodes, and territories defining the area the robot - is exploring. - """ - - def __init__(self): - self._open_nodes: List[Vector3] = [] - self._contact_nodes: List[Vector3] = [] - self._walls: List[Wall] = [] - self._cleared_territories: List[ClearedTerritory] = [] - self._collection_active: bool = False - - @staticmethod - def _set_color(color: List[float]): - """Modifies the OpenGL state's active material with a specific color - used on the specular & diffuse channels, with limited ambient. - - This function must be invoked inside the OpenGL render loop. - - :param color: the color to use for the material - """ - glColor(color) - glMaterialfv(GL_FRONT, GL_AMBIENT, [color[0] * 0.1, color[1] * 0.1, color[2] * 0.1, 1.0]) - glMaterialfv(GL_FRONT, GL_DIFFUSE, color) - glMaterialfv(GL_FRONT, GL_SPECULAR, color) - - glMaterialfv(GL_FRONT, GL_SHININESS, 10.0) - - @classmethod - def _render_points(cls, point_list: List[Vector3], size: float, color: List[float]): - """Draws a collection of points in the OpenGL 3D worldspace. - - This function must be invoked inside the OpenGL render loop. - - :param point_list: the points to render in the view context - :param size: the size in pixels of each point - :param color: the color to render the points - """ - glPointSize(size) - cls._set_color(color) - glBegin(GL_POINTS) - for point in point_list: - glVertex3f(point.x, point.y, point.z) - glEnd() - - @classmethod - def _render_circle(cls, center: Vector3, radius: float, sections: int, color: List[float]): - """Draws a circle out of dashed lines around a center point in the x-y plane. - - This function must be invoked inside the OpenGL render loop. - - :param center: the center point of the rendered circle - :param radius: the size of the rendered circle - :param sections: the number of vertices used in the dashed line circle - :param color: the color to render the points - """ - cls._set_color(color) - glBegin(GL_LINES) - for i in range(sections): - theta = pi * 2.0 * float(i) / float(sections - 1) - glVertex3f(center.x + cos(theta) * radius, - center.y + sin(theta) * radius, - center.z) - glEnd() - - @classmethod - def _render_wall(cls, wall: Wall, height: float, color: List[float]): - """Draws walls out of quads in the 3D Viewer. The walls are drawn a - constant height above their ground-plane points, as a convention. - - This function must be invoked inside the OpenGL render loop. - - :param wall_list: the walls to draw - :param radius: the size of the rendered circle - :param sections: the number of vertices used in the dashed line circle - :param color: the color to render the points - """ - cls._set_color(color) - - glBegin(GL_QUADS) - for wall_segment in wall.segments: - glNormal3f(wall_segment.normal.x, wall_segment.normal.y, wall_segment.normal.z) - glVertex3f(wall_segment.a.x, wall_segment.a.y, wall_segment.a.z) - glVertex3f(wall_segment.b.x, wall_segment.b.y, wall_segment.b.z) - glVertex3f(wall_segment.b.x, wall_segment.b.y, wall_segment.b.z + height) - glVertex3f(wall_segment.a.x, wall_segment.a.y, wall_segment.a.z + height) - glEnd() - - @property - def open_nodes(self) -> List[Vector3]: - """:return: Points on the map which have no proximity detections between - themselves and the proximity scan origin point that found them. - - These are safe points for the robot to drive to. - """ - return self._open_nodes - - @open_nodes.setter - def open_nodes(self, open_nodes: List[Vector3]): - self._open_nodes = open_nodes - - @property - def contact_nodes(self) -> List[Vector3]: - """:return: Points where an obstacle has been detected, but are too far - from other contacts to construct a wall from. - """ - return self._contact_nodes - - @property - def walls(self) -> List[Wall]: - """:return: Chains of points denoting barriers detected by the proximity - sensor. - """ - return self._walls - - @property - def cleared_territories(self) -> List[ClearedTerritory]: - """:return: Regions of space the robot has finished scanning. Any points - inside one of these regions can be considered accurate in terms of - detected boundaries. - """ - return self._cleared_territories - - @property - def collection_active(self) -> bool: - """:return: Whether or not proximity data should currently be collected.""" - return self._collection_active - - @collection_active.setter - def collection_active(self, collection_active: bool): - self._collection_active = collection_active - - def render(self, user_data_queue: mp.Queue = None): - """Low level OpenGL calls to render the current state in an opengl_viewer. This - code will be executed inside the viewer's process. - - Can be added to a viewer with the following code, telling the viewer to call this - whenever it redraws its geometry. - - .. code-block:: python - - robot.viewer_3d.add_render_call(my_map_state.render) - - :param user_data_queue: The queue to read MapState messages from the main process. - :type user_data_queue: mulitprocessing.Queue - """ - try: - latest: MapState = user_data_queue.get(False) - - self._open_nodes = latest._open_nodes # pylint: disable=protected-access - self._contact_nodes = latest._contact_nodes # pylint: disable=protected-access - self._walls = latest._walls # pylint: disable=protected-access - self._cleared_territories = latest._cleared_territories # pylint: disable=protected-access - self._collection_active = latest._collection_active # pylint: disable=protected-access - except mp.queues.Empty: - pass - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - glEnable(GL_BLEND) - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) - glDisable(GL_LIGHTING) - - if RENDER_METADATA_OBJECTS: - self._render_points(self._contact_nodes, NODE_CONTACT_RENDER_SIZE, NODE_CONTACT_RENDER_COLOR) - self._render_points(self._open_nodes, NODE_OPEN_RENDER_SIZE, NODE_OPEN_RENDER_COLOR) - - # render the nav point open sample - if self._cleared_territories and self._open_nodes: - self._render_circle(self._open_nodes[0], NAV_POINT_RENDER_SIZE, NAV_POINT_RENDER_SECTIONS, NAV_POINT_RENDER_COLOR) - - for cleared_territory in self._cleared_territories: - self._render_circle(cleared_territory.center, cleared_territory.radius, TERRITORY_RENDER_SECTIONS, TERRITORY_RENDER_COLOR) - - glEnable(GL_LIGHTING) - for wall in self._walls: - self._render_wall(wall, WALL_RENDER_HEIGHT_MM, WALL_RENDER_COLOR) diff --git a/examples/apps/proximity_mapper/proximity_mapper.py b/examples/apps/proximity_mapper/proximity_mapper.py deleted file mode 100755 index f67b0f0..0000000 --- a/examples/apps/proximity_mapper/proximity_mapper.py +++ /dev/null @@ -1,314 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Maps a region around Vector using the proximity sensor. - -Vector will turn in place and use his sensor to detect walls in his -local environment. These walls are displayed in the 3D Viewer. The -visualizer does not effect the robot's internal state or behavior. - -Vector expects this environment to be static - if objects are moved -he will have no knowledge of them. -""" - -import asyncio -import concurrent -from math import cos, sin, inf, acos -import os -import sys - -sys.path.append(os.path.join(os.path.dirname(__file__), 'lib')) -from proximity_mapper_state import ClearedTerritory, MapState, Wall, WallSegment # pylint: disable=wrong-import-position - -import anki_vector # pylint: disable=wrong-import-position -from anki_vector.util import parse_command_args, radians, degrees, distance_mm, speed_mmps, Vector3 # pylint: disable=wrong-import-position - -# Constants - -#: The maximum distance (in millimeters) the scan considers valid for a proximity respons. -#: Wall detection past this threshold will be disregarded, and an 'open' node will -#: be created at this distance instead. Increasing this value may degrade the -#: reliability of this program, see note below: -#: -#: NOTE: The proximity sensor works by sending a light pulse, and seeing how long that pulse takes -#: to reflect and return to the sensor. The proximity sensor does not specifically have a maximum -#: range, but will get unreliable results below a certain return signal strength. This return signal -#: is impacted by environmental conditions (such as the orientation and material of the detected obstacle) -#: as well as the distance. Additionally, increasing this radius will reduce the resolution of contact -#: points, necessitating changes to PROXIMITY_SCAN_SAMPLE_FREQUENCY_HZ and PROXIMITY_SCAN_BIND_THRESHOLD_MM -#: to maintain effective wall prediction. -PROXIMITY_SCAN_DISTANCE_THRESHOLD_MM = 300 - -#: The distance (in millimeters) to place an open node if no proximity results are detected along -#: a given line of sight. This should be smaller than the distance threshold, since these nodes -#: indicate safe points for the robot to drive to, and the robot's size should be taken into account -#: when estimating a maximum safe driving distance -PROXIMITY_SCAN_OPEN_NODE_DISTANCE_MM = 230 - -#: How frequently (in hertz) the robot checks proximity data while doing a scan. -PROXIMITY_SCAN_SAMPLE_FREQUENCY_HZ = 15.0 - -#: How long (in seconds) the robot spends doing it's 360 degree scan. -PROXIMITY_SCAN_TURN_DURATION_S = 10.0 - -#: How close (in millimeters) together two detected contact points need to be for the robot to -#: consider them part of a continuous wall. -PROXIMITY_SCAN_BIND_THRESHOLD_MM = 30.0 - -#: A delay (in seconds) the program waits after the scan finishes before shutting down. -#: This allows the user time to explore the mapped 3d environment in the viewer and can be -#: Tuned to any desired length. A value of 0.0 will prevent the viewer from closing. -PROXIMITY_EXPLORATION_SHUTDOWN_DELAY_S = 8.0 - - -# @TODO enable when testing shows it is ready to go -#: ACTIVELY_EXPLORE_SPACE can be activated to allow the robot to move -#: into an open space after scanning, and continue the process until all open -#: spaces are explored. -ACTIVELY_EXPLORE_SPACE = True -#: The speed (in millimeters/second) the robot drives while exploring. -EXPLORE_DRIVE_SPEED_MMPS = 40.0 -#: The speed (in degrees/second) the robot turns while exploring. -EXPLORE_TURN_SPEED_DPS = 90.0 - - -#: Takes a position in 3d space where a collection was detected, and adds it to the map state -#: by either creating a wall, adding to wall or storing a loose contact point. -async def add_proximity_contact_to_state(node_position: Vector3, state: MapState): - - # Comparison function for sorting points by distance. - def compare_distance(elem): - return (elem - node_position).magnitude_squared - - # Comparison function for sorting walls by distance using their head as a reference point. - def compare_head_distance(elem): - return (elem.vertices[0] - node_position).magnitude_squared - - # Comparison function for sorting walls by distance using their tail as a reference point. - def compare_tail_distance(elem): - return (elem.vertices[-1] - node_position).magnitude_squared - - # Sort all the loose contact nodes not yet incorporated into walls by - # their distance to our reading position. If the nearest one is within - # our binding threshold - store it as a viable wall creation partner. - # (infinity is used as a standin for 'nothing') - closest_contact_distance = inf - if state.contact_nodes: - state.contact_nodes.sort(key=compare_distance) - closest_contact_distance = (state.contact_nodes[0] - node_position).magnitude - if closest_contact_distance > PROXIMITY_SCAN_BIND_THRESHOLD_MM: - closest_contact_distance = inf - - # Sort all the walls both by head and tail distance from our sample - # if either of the results are within our binding threshold, store them - # as potential wall extension candidates for our sample. - # (infinity is used as a standin for 'nothing') - closest_head_distance = inf - closest_tail_distance = inf - if state.walls: - state.walls.sort(key=compare_tail_distance) - closest_tail_distance = (state.walls[0].vertices[-1] - node_position).magnitude - if closest_tail_distance > PROXIMITY_SCAN_BIND_THRESHOLD_MM: - closest_tail_distance = inf - - state.walls.sort(key=compare_head_distance) - closest_head_distance = (state.walls[0].vertices[0] - node_position).magnitude - if closest_head_distance > PROXIMITY_SCAN_BIND_THRESHOLD_MM: - closest_head_distance = inf - - # Create a new wall if a loose contact node is in bind range and - # is closer than any existing wall. The contact node will be removed. - if closest_contact_distance <= PROXIMITY_SCAN_BIND_THRESHOLD_MM and closest_contact_distance < closest_head_distance and closest_contact_distance < closest_tail_distance: - state.walls.append(Wall(WallSegment(state.contact_nodes[0], node_position))) - state.contact_nodes.pop(0) - - # Extend a wall if it's head is within bind range and is closer than - # any loose contacts or wall tails. - elif closest_head_distance <= PROXIMITY_SCAN_BIND_THRESHOLD_MM and closest_head_distance < closest_contact_distance and closest_head_distance < closest_tail_distance: - state.walls[0].insert_head(node_position) - - # Extend a wall if it's tail is within bind range and is closer than - # any loose contacts or wall heads. - elif closest_tail_distance <= PROXIMITY_SCAN_BIND_THRESHOLD_MM and closest_tail_distance < closest_contact_distance and closest_tail_distance < closest_head_distance: - state.walls.sort(key=compare_tail_distance) - state.walls[0].insert_tail(node_position) - - # If nothing was found to bind with, store the sample as a loose contact node. - else: - state.contact_nodes.append(node_position) - - -#: Takes a position in 3d space and adds it to the map state as an open node -async def add_proximity_non_contact_to_state(node_position: Vector3, state: MapState): - # Check to see if the uncontacted sample is inside of any area considered already explored. - is_open_unexplored = True - for ct in state.cleared_territories: - if (node_position - ct.center).magnitude < ct.radius: - is_open_unexplored = False - - # If the uncontacted sample is in unfamiliar ground, store it as an open node. - if is_open_unexplored: - state.open_nodes.append(node_position) - - -#: Modifies the map state with the details of a proximity reading -async def analyze_proximity_sample(reading: anki_vector.proximity.ProximitySensorData, robot: anki_vector.robot.Robot, state: MapState): - # Check if the reading meets the engine's metrics for valid, and that its within our specified distance threshold. - reading_contacted = reading.is_valid and reading.distance.distance_mm < PROXIMITY_SCAN_DISTANCE_THRESHOLD_MM - - if reading_contacted: - # The distance will either be the reading data, or our threshold distance if the reading is considered uncontacted. - reading_distance = reading.distance.distance_mm if reading_contacted else PROXIMITY_SCAN_DISTANCE_THRESHOLD_MM - - # Convert the distance to a 3d position in worldspace. - reading_position = Vector3( - robot.pose.position.x + cos(robot.pose_angle_rad) * reading_distance, - robot.pose.position.y + sin(robot.pose_angle_rad) * reading_distance, - robot.pose.position.z) - - await add_proximity_contact_to_state(reading_position, state) - else: - # Convert the distance to a 3d position in worldspace. - safe_driving_position = Vector3( - robot.pose.position.x + cos(robot.pose_angle_rad) * PROXIMITY_SCAN_OPEN_NODE_DISTANCE_MM, - robot.pose.position.y + sin(robot.pose_angle_rad) * PROXIMITY_SCAN_OPEN_NODE_DISTANCE_MM, - robot.pose.position.z) - - await add_proximity_non_contact_to_state(safe_driving_position, state) - - -#: repeatedly collects proximity data sample and converts them to nodes and walls for the map state -async def collect_proximity_data_loop(robot: anki_vector.robot.Robot, future: concurrent.futures.Future, state: MapState): - try: - scan_interval = 1.0 / PROXIMITY_SCAN_SAMPLE_FREQUENCY_HZ - - # Runs until the collection_active flag is cleared. - # This flag is cleared external to this function. - while state.collection_active: - # Collect proximity data from the sensor. - reading = robot.proximity.last_sensor_reading - if reading is not None: - await analyze_proximity_sample(reading, robot, state) - robot.viewer_3d.user_data_queue.put(state) - await asyncio.sleep(scan_interval) - - # Exceptions raised in this process are ignored, unless we set them on the future, and then run future.result() at a later time - except Exception as e: # pylint: disable=broad-except - future.set_exception(e) - finally: - future.set_result(state) - - -#: Updates the map state by rotating 360 degrees and collecting/applying proximity data samples. -async def scan_area(robot: anki_vector.robot.Robot, state: MapState): - collect_future = concurrent.futures.Future() - - # The collect_proximity_data task relies on this external trigger to know when its finished. - state.collection_active = True - - # Activate the collection task while the robot turns in place. - collect_task = robot.conn.loop.create_task(collect_proximity_data_loop(robot, collect_future, state)) - - # Turn around in place, then send the signal to kill the collection task. - robot.behavior.turn_in_place(angle=degrees(360.0), speed=degrees(360.0 / PROXIMITY_SCAN_TURN_DURATION_S)) - state.collection_active = False - - # Wait for the collection task to finish. - robot.conn.run_coroutine(collect_task) - # While the result of the task is not used, this call will propagate any exceptions that - # occured in the task, allowing for debug visibility. - collect_future.result() - - -#: Top level call to perform exploration and environment mapping -async def map_explorer(robot: anki_vector.robot.Robot): - # Drop the lift, so that it does not block the proximity sensor - robot.behavior.set_lift_height(0.0) - - # Create the map state, and add it's rendering function to the viewer's render pipeline - state = MapState() - robot.viewer_3d.add_render_call(state.render) - - # Comparison function used for sorting which open nodes are the furthest from all existing - # walls and loose contacts. - # (Using 1/r^2 to respond strongly to small numbers of close contact and weaking to many distant contacts) - def open_point_sort_func(position: Vector3): - proximity_sum = 0 - for p in state.contact_nodes: - proximity_sum = proximity_sum + 1 / (p - position).magnitude_squared - for c in state.walls: - for p in c.vertices: - proximity_sum = proximity_sum + 1 / (p - position).magnitude_squared - return proximity_sum - - # Loop until running out of open samples to navigate to, - # or if the process has yet to start (indicated by a lack of cleared_territories). - while (state.open_nodes and ACTIVELY_EXPLORE_SPACE) or not state.cleared_territories: - if robot.pose: - # Delete any open samples range of the robot. - state.open_nodes = [position for position in state.open_nodes if (position - robot.pose.position).magnitude > PROXIMITY_SCAN_DISTANCE_THRESHOLD_MM] - - # Collect map data for the robot's current location. - await scan_area(robot, state) - - # Add where the robot is to the map's cleared territories. - state.cleared_territories.append(ClearedTerritory(robot.pose.position, PROXIMITY_SCAN_DISTANCE_THRESHOLD_MM)) - - # @TODO: This whole block should ideally be replaced with the go_to_pose actions when that is ready to go. - # Alternatively, the turn&drive commands can be modified to respond to collisions by cancelling. After - # either change, ACTIVELY_EXPLORE_SPACE should be defaulted True - if ACTIVELY_EXPLORE_SPACE and state.open_nodes: - # Sort the open nodes and find our next navigation point. - state.open_nodes.sort(key=open_point_sort_func) - nav_point = state.open_nodes[0] - - # Calculate the distance and direction of this next navigation point. - nav_point_delta = Vector3( - nav_point.x - robot.pose.position.x, - nav_point.y - robot.pose.position.y, - 0) - nav_distance = nav_point_delta.magnitude - nav_direction = nav_point_delta.normalized - - # Convert the nav_direction into a turn angle relative to the robot's current facing. - robot_forward = Vector3(*robot.pose.rotation.to_matrix().forward_xyz).normalized - turn_angle = acos(nav_direction.dot(robot_forward)) - if nav_direction.cross(robot_forward).z > 0: - turn_angle = -turn_angle - - # Turn toward the nav point, and drive to it. - robot.behavior.turn_in_place(angle=radians(turn_angle), speed=degrees(EXPLORE_TURN_SPEED_DPS)) - robot.behavior.drive_straight(distance=distance_mm(nav_distance), speed=speed_mmps(EXPLORE_DRIVE_SPEED_MMPS)) - - if PROXIMITY_EXPLORATION_SHUTDOWN_DELAY_S == 0.0: - while True: - await asyncio.sleep(1.0) - else: - print('finished exploring - waiting an additional {0} seconds, then shutting down'.format(PROXIMITY_EXPLORATION_SHUTDOWN_DELAY_S)) - await asyncio.sleep(PROXIMITY_EXPLORATION_SHUTDOWN_DELAY_S) - - -if __name__ == '__main__': - # Connect to the robot - args = parse_command_args() - with anki_vector.Robot(args.serial, - enable_camera_feed=True, - show_viewer=True, - enable_nav_map_feed=False, - show_3d_viewer=True) as robotInstance: - robotInstance.behavior.drive_off_charger() - loop = asyncio.get_event_loop() - loop.run_until_complete(map_explorer(robotInstance)) diff --git a/examples/apps/remote_control/lib/__init__.py b/examples/apps/remote_control/lib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/apps/remote_control/lib/flask_helpers.py b/examples/apps/remote_control/lib/flask_helpers.py deleted file mode 100644 index f6b5913..0000000 --- a/examples/apps/remote_control/lib/flask_helpers.py +++ /dev/null @@ -1,110 +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 -# -# https://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. - -"""Wrappers and helpers for using Flask with Vector. - -Flask is a Python web framework. remote_control_vector.py and other scripts may use -these utility functions to interact with a web browser. -""" - -import logging -import sys -from threading import Thread -import webbrowser -from time import sleep -from io import BytesIO -try: - from flask import make_response, Response, send_file -except ImportError: - sys.exit("Cannot import from flask: Do `pip3 install --user flask` to install") - - -def _delayed_open_web_browser(url, delay, new=0, autoraise=True, specific_browser=None): - """ - Spawn a thread and call sleep_and_open_web_browser from within it so that main thread can keep executing at the - same time. Insert a small sleep before opening a web-browser - this gives Flask a chance to start running before the browser starts requesting data from Flask. - """ - - def _sleep_and_open_web_browser(url, delay, new, autoraise, specific_browser): - sleep(delay) - browser = webbrowser - - # E.g. On OSX the following would use the Chrome browser app from that location - # specific_browser = 'open -a /Applications/Google\ Chrome.app %s' - if specific_browser: - browser = webbrowser.get(specific_browser) - - browser.open(url, new=new, autoraise=autoraise) - - thread = Thread(target=_sleep_and_open_web_browser, - kwargs=dict(url=url, new=new, autoraise=autoraise, delay=delay, specific_browser=specific_browser)) - thread.daemon = True # Force to quit on main quitting - thread.start() - - -def run_flask(flask_app, host_ip="127.0.0.1", host_port=5000, enable_flask_logging=False, - open_page=True, open_page_delay=1.0): - """ - Run the Flask webserver on specified host and port - optionally also open that same host:port page in your browser to connect - """ - - if not enable_flask_logging: - # disable logging in Flask (it's enabled by default) - log = logging.getLogger('werkzeug') - log.setLevel(logging.ERROR) - - if open_page: - # we add a delay (dispatched in another thread) to open the page so that the flask webserver is open - # before the webpage requests any data - _delayed_open_web_browser("http://" + host_ip + ":" + str(host_port), delay=open_page_delay) - - flask_app.run(host=host_ip, port=host_port, use_evalex=False, threaded=True) - - -def shutdown_flask(request): - func = request.environ.get('werkzeug.server.shutdown') - if func is None: - sys.exit('SDK Shutdown') - func() - - -def stream_video(streaming_function): - return Response(streaming_function(), mimetype='multipart/x-mixed-replace; boundary=frame') - - -def make_uncached_response(in_file): - response = make_response(in_file) - response.headers['Pragma-Directive'] = 'no-cache' - response.headers['Cache-Directive'] = 'no-cache' - response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate' - response.headers['Pragma'] = 'no-cache' - response.headers['Expires'] = '0' - return response - - -def serve_pil_image(pil_img, serve_as_jpeg=False, jpeg_quality=70): - """Convert PIL image to relevant image file and send it""" - img_io = BytesIO() - - if serve_as_jpeg: - pil_img.save(img_io, 'JPEG', quality=jpeg_quality) - img_io.seek(0) - return make_uncached_response(send_file(img_io, mimetype='image/jpeg', - add_etags=False)) - pil_img.save(img_io, 'PNG') - img_io.seek(0) - return make_uncached_response(send_file(img_io, mimetype='image/png', - add_etags=False)) diff --git a/examples/face_images/cozmo_image.jpg b/examples/face_images/cozmo_image.jpg deleted file mode 100644 index 690c84b830d8f90cb4dcc1818d49042deb3d5ce6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6618 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 diff --git a/examples/tutorials/01_hello_world.py b/examples/tutorials/01_hello_world.py deleted file mode 100755 index d7d57ce..0000000 --- a/examples/tutorials/01_hello_world.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Hello World - -Make Vector say 'Hello World' in this simple Vector SDK example program. -""" - -import anki_vector - - -def main(): - args = anki_vector.util.parse_command_args() - with anki_vector.Robot(args.serial) as robot: - print("Say 'Hello World'...") - robot.say_text("Hello World") - - -if __name__ == "__main__": - main() diff --git a/examples/tutorials/02_drive_square.py b/examples/tutorials/02_drive_square.py deleted file mode 100755 index c1622dd..0000000 --- a/examples/tutorials/02_drive_square.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Make Vector drive in a square. - -Make Vector drive in a square by going forward and turning left 4 times in a row. -""" - -import anki_vector -from anki_vector.util import degrees, distance_mm, speed_mmps - - -def main(): - args = anki_vector.util.parse_command_args() - - # The robot drives straight, stops and then turns around - with anki_vector.Robot(args.serial) as robot: - robot.behavior.drive_off_charger() - - # Use a "for loop" to repeat the indented code 4 times - # Note: the _ variable name can be used when you don't need the value - for _ in range(4): - print("Drive Vector straight...") - robot.behavior.drive_straight(distance_mm(200), speed_mmps(50)) - - print("Turn Vector in place...") - robot.behavior.turn_in_place(degrees(90)) - - -if __name__ == "__main__": - main() diff --git a/examples/tutorials/03_motors.py b/examples/tutorials/03_motors.py deleted file mode 100755 index 0a1414a..0000000 --- a/examples/tutorials/03_motors.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Drive Vector's wheels, lift and head motors directly - -This is an example of how you can also have low-level control of Vector's motors -(wheels, lift and head) for fine-grained control and ease of controlling -multiple things at once. -""" - -import time -import anki_vector - - -def main(): - args = anki_vector.util.parse_command_args() - with anki_vector.Robot(args.serial) as robot: - robot.behavior.drive_off_charger() - - # Tell the head motor to start lowering the head (at 5 radians per second) - print("Lower Vector's head...") - robot.motors.set_head_motor(-5.0) - - # Tell the lift motor to start lowering the lift (at 5 radians per second) - print("Lower Vector's lift...") - robot.motors.set_lift_motor(-5.0) - - # Tell Vector to drive the left wheel at 25 mmps (millimeters per second), - # and the right wheel at 50 mmps (so Vector will drive Forwards while also - # turning to the left - print("Set Vector's wheel motors...") - robot.motors.set_wheel_motors(25, 50) - - # wait for 3 seconds (the head, lift and wheels will move while we wait) - time.sleep(3) - - # Tell the head motor to start raising the head (at 5 radians per second) - print("Raise Vector's head...") - robot.motors.set_head_motor(5) - - # Tell the lift motor to start raising the lift (at 5 radians per second) - print("Raise Vector's lift...") - robot.motors.set_lift_motor(5) - - # Tell Vector to drive the left wheel at 50 mmps (millimeters per second), - # and the right wheel at -50 mmps (so Vector will turn in-place to the right) - print("Set Vector's wheel motors...") - robot.motors.set_wheel_motors(50, -50) - - # Wait for 3 seconds (the head, lift and wheels will move while we wait) - time.sleep(3) - - # Stop the motors, which unlocks the tracks - robot.motors.set_wheel_motors(0, 0) - robot.motors.set_lift_motor(0) - robot.motors.set_head_motor(0) - - -if __name__ == "__main__": - main() diff --git a/examples/tutorials/04_animation.py b/examples/tutorials/04_animation.py deleted file mode 100755 index 614b231..0000000 --- a/examples/tutorials/04_animation.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Play an animation on Vector -""" - -import anki_vector - - -def main(): - args = anki_vector.util.parse_command_args() - with anki_vector.Robot(args.serial) as robot: - robot.behavior.drive_off_charger() - - # Play an animation via its name. - # - # Warning: Future versions of the app might change these, so for future-proofing - # we recommend using play_animation_trigger when it becomes available. - # - # See the remote_control.py example in apps for an easy way to see - # the available animations. - animation = 'anim_pounce_success_02' - print("Playing animation by name: " + animation) - robot.anim.play_animation(animation) - - -if __name__ == "__main__": - main() diff --git a/examples/tutorials/05_play_behaviors.py b/examples/tutorials/05_play_behaviors.py deleted file mode 100755 index 536379d..0000000 --- a/examples/tutorials/05_play_behaviors.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Tell Vector to drive on and off the charger. -""" - -import anki_vector - - -def main(): - args = anki_vector.util.parse_command_args() - - with anki_vector.Robot(args.serial) as robot: - print("Drive Vector onto charger...") - robot.behavior.drive_on_charger() - - print("Drive Vector off of charger...") - robot.behavior.drive_off_charger() - - -if __name__ == '__main__': - main() diff --git a/examples/tutorials/06_face_image.py b/examples/tutorials/06_face_image.py deleted file mode 100755 index 6106a9c..0000000 --- a/examples/tutorials/06_face_image.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Display an image on Vector's face -""" - -import os -import sys -import time - -try: - from PIL import Image -except ImportError: - sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install") - -import anki_vector -from anki_vector.util import degrees - - -def main(): - args = anki_vector.util.parse_command_args() - - with anki_vector.Robot(args.serial) as robot: - # If necessary, move Vector's Head and Lift to make it easy to see his face - robot.behavior.set_head_angle(degrees(45.0)) - robot.behavior.set_lift_height(0.0) - - current_directory = os.path.dirname(os.path.realpath(__file__)) - image_path = os.path.join(current_directory, "..", "face_images", "cozmo_image.jpg") - - # Load an image - image_file = Image.open(image_path) - - # Convert the image to the format used by the Screen - print("Display image on Vector's face...") - screen_data = anki_vector.screen.convert_image_to_screen_data(image_file) - robot.screen.set_screen_with_image_data(screen_data, 4.0) - time.sleep(5) - - -if __name__ == "__main__": - main() diff --git a/examples/tutorials/07_dock_with_cube.py b/examples/tutorials/07_dock_with_cube.py deleted file mode 100755 index c7f1c4c..0000000 --- a/examples/tutorials/07_dock_with_cube.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Tell Vector to drive up to a seen cube. - -This example demonstrates Vector driving to and docking with a cube, without -picking it up. Vector will line his arm hooks up with the cube so that they are -inserted into the cube's corners. - -You must place a cube in front of Vector so that he can see it. -""" - -import anki_vector -from anki_vector.util import degrees - - -def main(): - args = anki_vector.util.parse_command_args() - - docking_result = None - with anki_vector.Robot(args.serial) as robot: - robot.behavior.drive_off_charger() - - # If necessary, move Vector's Head and Lift down - 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: - print("Begin cube docking...") - dock_response = robot.behavior.dock_with_cube( - robot.world.connected_light_cube, - num_retries=3) - if dock_response: - docking_result = dock_response.result - - robot.world.disconnect_cube() - - if docking_result: - if docking_result.code != anki_vector.messaging.protocol.ActionResult.ACTION_RESULT_SUCCESS: - print("Cube docking failed with code {0} ({1})".format(str(docking_result).rstrip('\n\r'), docking_result.code)) - else: - print("Cube docking failed.") - - -if __name__ == "__main__": - main() diff --git a/examples/tutorials/08_drive_to_cliff_and_back_up.py b/examples/tutorials/08_drive_to_cliff_and_back_up.py deleted file mode 100755 index 9991ac6..0000000 --- a/examples/tutorials/08_drive_to_cliff_and_back_up.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Make Vector drive to a cliff and back up. - -Place the robot about a foot from a "cliff" (such as a tabletop edge), -then run this script. - -This tutorial is an advanced example that shows the SDK's integration -with the Vector behavior system. - -The Vector behavior system uses an order of prioritizations to determine -what the robot will do next. The highest priorities in the behavior -system including the following: -* When Vector reaches a cliff, he will back up to avoid falling. -* When Vector is low on battery, he will start searching for his charger -and self-dock. - -When the SDK is running at a lower priority level than high priorities -like cliff and low battery, an SDK program can lose its ability to -control the robot when a cliff if reached or when battery is low. - -This example shows how, after reaching a cliff, the SDK program can -re-request control so it can continue controlling the robot after -reaching the cliff. -""" - -import anki_vector -from anki_vector.util import distance_mm, speed_mmps - - -def main(): - args = anki_vector.util.parse_command_args() - - with anki_vector.Robot(args.serial) as robot: - print("Vector SDK has behavior control...") - robot.behavior.drive_off_charger() - - print("Drive Vector straight until he reaches cliff...") - # Once robot reaches cliff, he will play his typical cliff reactions. - robot.behavior.drive_straight(distance_mm(5000), speed_mmps(100)) - - robot.conn.run_coroutine(robot.conn.control_lost_event.wait()).result() - - print("Lost SDK behavior control. Request SDK behavior control again...") - robot.conn.request_control() - - print("Drive Vector backward away from the cliff...") - robot.behavior.drive_straight(distance_mm(-300), speed_mmps(100)) - - -if __name__ == "__main__": - main() diff --git a/examples/tutorials/09_show_photo.py b/examples/tutorials/09_show_photo.py deleted file mode 100755 index 11e2907..0000000 --- a/examples/tutorials/09_show_photo.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Show a photo taken by Vector. - -Grabs the pictures off of Vector and open them via PIL. - -Before running this script, please make sure you have successfully -had Vector take a photo by saying, "Hey Vector! Take a photo." -""" - -import io -import sys - -try: - from PIL import Image -except ImportError: - sys.exit("Cannot import from PIL: Do `pip3 install --user Pillow` to install") - -import anki_vector - - -def main(): - args = anki_vector.util.parse_command_args() - with anki_vector.Robot(args.serial) as robot: - if len(robot.photos.photo_info) == 0: - print('\n\nNo photos found on Vector. Ask him to take a photo first by saying, "Hey Vector! Take a photo."\n\n') - return - for photo in robot.photos.photo_info: - print(f"Opening photo {photo.photo_id}") - val = robot.photos.get_photo(photo.photo_id) - image = Image.open(io.BytesIO(val.image)) - image.show() - - -if __name__ == "__main__": - main() diff --git a/examples/tutorials/10_eye_color.py b/examples/tutorials/10_eye_color.py deleted file mode 100755 index 988ce80..0000000 --- a/examples/tutorials/10_eye_color.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Set Vector's eye color. -""" - -import time -import anki_vector - - -def main(): - args = anki_vector.util.parse_command_args() - - with anki_vector.Robot(args.serial) as robot: - print("Set Vector's eye color to purple...") - robot.behavior.set_eye_color(hue=0.83, saturation=0.76) - - print("Sleep 5 seconds...") - time.sleep(5) - - -if __name__ == '__main__': - main() diff --git a/examples/tutorials/11_face_event_subscription.py b/examples/tutorials/11_face_event_subscription.py deleted file mode 100755 index 0391303..0000000 --- a/examples/tutorials/11_face_event_subscription.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Wait for Vector to see a face, and then print output to the console. - -This script demonstrates how to set up a listener for an event. It -subscribes to event 'robot_observed_face'. When that event is dispatched, -method 'on_robot_observed_face' is called, which prints text to the console. -Vector will also say "I see a face" one time, and the program will exit when -he finishes speaking. -""" - -import functools -import threading - -import anki_vector -from anki_vector.events import Events -from anki_vector.util import degrees - -said_text = False - - -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: - - # If necessary, move Vector's Head and Lift to make it easy to see his face - robot.behavior.set_head_angle(degrees(45.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 ------") - - try: - if not evt.wait(timeout=5): - print("------ Vector never saw your face! ------") - 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 deleted file mode 100755 index 7ef8ca3..0000000 --- a/examples/tutorials/12_wake_word_subscription.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""Wait for Vector to hear "Hey Vector!" and then play an animation. - -The wake_word event only is dispatched when the SDK program has -not requested behavior control. After the robot hears "Hey Vector!" -and the event is received, you can then request behavior control -and control the robot. See the 'requires_behavior_control' method in -connection.py for more information. -""" - -import functools -import threading - -import anki_vector -from anki_vector.events import Events - -wake_word_heard = False - - -def main(): - evt = threading.Event() - - 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 - 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 ------') - - try: - if not evt.wait(timeout=10): - print('------ Vector never heard "Hey Vector!" ------') - except KeyboardInterrupt: - pass - - -if __name__ == '__main__': - main() diff --git a/examples/tutorials/13_custom_objects.py b/examples/tutorials/13_custom_objects.py deleted file mode 100755 index a7398a8..0000000 --- a/examples/tutorials/13_custom_objects.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python3 - -# 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 -# -# https://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. - -"""This example demonstrates how you can define custom objects. - -The example defines several custom objects (2 cubes, a wall and a box). When -Vector sees the markers for those objects he will report that he observed an -object of that size and shape there. - -You can adjust the markers, marker sizes, and object sizes to fit whatever -object you have and the exact size of the markers that you print out. -""" - -import time - -import anki_vector -from anki_vector.objects import CustomObjectMarkers, CustomObjectTypes - - -def handle_object_appeared(event_type, event): - # This will be called whenever an EvtObjectAppeared is dispatched - - # whenever an Object comes into view. - print(f"--------- Vector started seeing an object --------- \n{event.obj}") - - -def handle_object_disappeared(event_type, event): - # This will be called whenever an EvtObjectDisappeared is dispatched - - # whenever an Object goes out of view. - print(f"--------- Vector stopped seeing an object --------- \n{event.obj}") - - -def main(): - args = anki_vector.util.parse_command_args() - with anki_vector.Robot(args.serial, - default_logging=False, - show_viewer=True, - show_3d_viewer=True, - enable_camera_feed=True, - enable_custom_object_detection=True, - enable_nav_map_feed=True) as robot: - # Add event handlers for whenever Vector sees a new object - robot.events.subscribe(handle_object_appeared, anki_vector.events.Events.object_appeared) - robot.events.subscribe(handle_object_disappeared, anki_vector.events.Events.object_disappeared) - - # define a unique cube (44mm x 44mm x 44mm) (approximately the same size as Vector's light cube) - # with a 50mm x 50mm Circles2 image on every face. Note that marker_width_mm and marker_height_mm - # parameter values must match the dimensions of the printed marker. - cube_obj = robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType00, - marker=CustomObjectMarkers.Circles2, - size_mm=44.0, - marker_width_mm=50.0, - marker_height_mm=50.0, - is_unique=True) - - # define a unique cube (88mm x 88mm x 88mm) (approximately 2x the size of Vector's light cube) - # with a 50mm x 50mm Circles3 image on every face. - big_cube_obj = robot.world.define_custom_cube(custom_object_type=CustomObjectTypes.CustomType01, - marker=CustomObjectMarkers.Circles3, - size_mm=88.0, - marker_width_mm=50.0, - marker_height_mm=50.0, - is_unique=True) - - # define a unique wall (150mm x 120mm (x10mm thick for all walls) - # with a 50mm x 30mm Triangles2 image on front and back - wall_obj = robot.world.define_custom_wall(custom_object_type=CustomObjectTypes.CustomType02, - marker=CustomObjectMarkers.Triangles2, - width_mm=150, - height_mm=120, - marker_width_mm=50, - marker_height_mm=30, - is_unique=True) - - # define a unique box (20mm deep x 20mm width x20mm tall) - # with a different 50mm x 50mm image on each of the 6 faces - box_obj = robot.world.define_custom_box(custom_object_type=CustomObjectTypes.CustomType03, - marker_front=CustomObjectMarkers.Diamonds2, # front - marker_back=CustomObjectMarkers.Hexagons2, # back - marker_top=CustomObjectMarkers.Hexagons3, # top - marker_bottom=CustomObjectMarkers.Hexagons4, # bottom - marker_left=CustomObjectMarkers.Triangles3, # left - marker_right=CustomObjectMarkers.Triangles4, # right - depth_mm=20.0, - width_mm=20.0, - height_mm=20.0, - marker_width_mm=50.0, - marker_height_mm=50.0, - is_unique=True) - - if ((cube_obj is not None) and (big_cube_obj is not None) and - (wall_obj is not None) and (box_obj is not None)): - print("All objects defined successfully!") - else: - print("One or more object definitions failed!") - return - - print("\n\nShow a marker specified in the Python script to Vector and you will see the related 3d objects\n" - "display in Vector's 3d_viewer window. You will also see messages print every time a custom object\n" - "enters or exits Vector's view. Markers can be found from the docs under CustomObjectMarkers.\n\n") - - try: - while True: - time.sleep(0.5) - except KeyboardInterrupt: - pass - - -if __name__ == "__main__": - main() diff --git a/examples/apps/remote_control/remote_control.py b/remote_control/remote_control.py similarity index 99% rename from examples/apps/remote_control/remote_control.py rename to remote_control/remote_control.py index 99e5529..276e891 100755 --- a/examples/apps/remote_control/remote_control.py +++ b/remote_control/remote_control.py @@ -608,7 +608,7 @@ def handle_setFreeplayEnabled(): if flask_app.remote_control_vector: isFreeplayEnabled = message['isFreeplayEnabled'] connection = flask_app.remote_control_vector.vector.conn - connection.request_control(enable=(not isFreeplayEnabled)) + connection.update(enabled=(not isFreeplayEnabled)) return "" diff --git a/requirements.txt b/requirements.txt index b015a67..3bf4902 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,3 @@ -aiogrpc>=1.4 -cryptography +anki_vector flask -googleapis-common-protos -numpy>=1.11 -opencv-python>=3.4 -Pillow>=3.3 -requests \ No newline at end of file +PIL \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index bc8de24..0000000 --- a/setup.py +++ /dev/null @@ -1,86 +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 -# -# https://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. - -""" -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. Find out more at https://developer.anki.com - -Vector SDK documentation: https://developer.anki.com/vector/docs/ - -Official developer forum: https://forums.anki.com/ - -Requirements: - * Python 3.6.1 or later -""" - -import os.path -import sys -from setuptools import setup - -if sys.version_info < (3, 6, 1): - sys.exit('The Vector SDK requires Python 3.6.1 or later') - -HERE = os.path.abspath(os.path.dirname(__file__)) - -def fetch_version(): - """Get the version from the package""" - with open(os.path.join(HERE, 'anki_vector', 'version.py')) as version_file: - versions = {} - exec(version_file.read(), versions) - return versions - -VERSION_DATA = fetch_version() -VERSION = VERSION_DATA['__version__'] - -def get_requirements(): - """Load the requirements from requirements.txt into a list""" - reqs = [] - with open(os.path.join(HERE, 'requirements.txt')) as requirements_file: - for line in requirements_file: - reqs.append(line.strip()) - return reqs - -setup( - name='anki_vector', - version=VERSION, - 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', - author_email='developer@anki.com', - license='Apache License, Version 2.0', - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Libraries', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python :: 3.6', - ], - zip_safe=True, - keywords='anki vector robot robotics sdk ai vision'.split(), - packages=['anki_vector', 'anki_vector.configure', 'anki_vector.messaging', 'anki_vector.opengl'], - package_data={ - 'anki_vector': ['LICENSE.txt', 'opengl/assets/*.obj', 'opengl/assets/*.mtl', 'opengl/assets/*.jpg', - 'opengl/assets/LICENSE.txt'] - }, - install_requires=get_requirements(), - extras_require={ - '3dviewer': ['PyOpenGL>=3.1'], - 'docs': ['sphinx', 'sphinx_rtd_theme', 'sphinx_autodoc_typehints'], - 'experimental': ['keras', 'scikit-learn', 'scipy', 'tensorflow'], - 'test': ['pytest', 'requests', 'requests_toolbelt'], - } -) From 59737c33de9c320ac636c80f250597642390e396 Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Tue, 8 Jan 2019 21:19:11 -0500 Subject: [PATCH 02/12] Move the code in a source directory --- remote_control/remote_control.py => src/controller.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename remote_control/remote_control.py => src/controller.py (100%) diff --git a/remote_control/remote_control.py b/src/controller.py similarity index 100% rename from remote_control/remote_control.py rename to src/controller.py From 72bf3f368ad822d9a1e2a31571a27a3bcb301705 Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Tue, 8 Jan 2019 21:25:41 -0500 Subject: [PATCH 03/12] Add a setup file for installing the controller --- README.md | 1 + setup.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 README.md create mode 100644 setup.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..c16d793 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Vector Remote Controller diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c64de18 --- /dev/null +++ b/setup.py @@ -0,0 +1,20 @@ +from setuptools import setup, find_packages +from os import path + +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the README file +with open(path.join(here, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name='vector-controller', + version='0.5.1', + description='A webpage-based controller for Anki Vector', + long_description=long_description, + long_description_content_type='text/markdown', + url='https://github.com/krconv/vector-controller', + author='Kodey Converse', + author_email='kodey@krconv.com', + packages=find_packages(exclude=['tests']), +) \ No newline at end of file From 28e70fb81d53b5cb3dc472b31622044b79769f20 Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Tue, 8 Jan 2019 21:51:51 -0500 Subject: [PATCH 04/12] Ignore pipenv files --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 22b5e3d..efc9a6b 100644 --- a/.gitignore +++ b/.gitignore @@ -113,4 +113,8 @@ venv.bak/ dmypy.json # Pyre type checker -.pyre/ \ No newline at end of file +.pyre/ + +# pipenv +Pipfile +Pipfile.lock From 1c9e615e5eab7fb34331bf1f9a7ef2f85ddc9daa Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Tue, 8 Jan 2019 21:53:34 -0500 Subject: [PATCH 05/12] Create a command line program for starting the Vector controller --- setup.py | 6 ++++++ vector_controller/__main__.py | 14 ++++++++++++++ {src => vector_controller}/controller.py | 13 ++----------- 3 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 vector_controller/__main__.py rename {src => vector_controller}/controller.py (98%) diff --git a/setup.py b/setup.py index c64de18..9d44d71 100644 --- a/setup.py +++ b/setup.py @@ -17,4 +17,10 @@ author='Kodey Converse', author_email='kodey@krconv.com', packages=find_packages(exclude=['tests']), + entry_points={ + 'console_scripts': [ + 'vector_controller=vector_controller.__main__:main', + ], + }, + ) \ No newline at end of file diff --git a/vector_controller/__main__.py b/vector_controller/__main__.py new file mode 100644 index 0000000..11eb712 --- /dev/null +++ b/vector_controller/__main__.py @@ -0,0 +1,14 @@ +from . import controller + +import anki_vector + +import sys + + +def main(): + try: + controller.run() + except KeyboardInterrupt as e: + pass + except anki_vector.exceptions.VectorConnectionException as e: + sys.exit("A connection error occurred: %s" % e) \ No newline at end of file diff --git a/src/controller.py b/vector_controller/controller.py similarity index 98% rename from src/controller.py rename to vector_controller/controller.py index 276e891..1b4448e 100755 --- a/src/controller.py +++ b/vector_controller/controller.py @@ -23,7 +23,7 @@ import json import sys import time -from lib import flask_helpers +from .lib import flask_helpers import anki_vector from anki_vector import util @@ -670,13 +670,4 @@ def run(): robot.behavior.drive_off_charger() - flask_helpers.run_flask(flask_app) - - -if __name__ == '__main__': - try: - run() - except KeyboardInterrupt as e: - pass - except anki_vector.exceptions.VectorConnectionException as e: - sys.exit("A connection error occurred: %s" % e) + flask_helpers.run_flask(flask_app) \ No newline at end of file From 9714b4b66960926a7f67aedc1a369f63cb15fa37 Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Tue, 8 Jan 2019 22:13:47 -0500 Subject: [PATCH 06/12] Add helper functions for working with flask --- vector_controller/lib/__init__.py | 0 vector_controller/lib/flask_helpers.py | 110 +++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 vector_controller/lib/__init__.py create mode 100644 vector_controller/lib/flask_helpers.py diff --git a/vector_controller/lib/__init__.py b/vector_controller/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vector_controller/lib/flask_helpers.py b/vector_controller/lib/flask_helpers.py new file mode 100644 index 0000000..f6b5913 --- /dev/null +++ b/vector_controller/lib/flask_helpers.py @@ -0,0 +1,110 @@ +# 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 +# +# https://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. + +"""Wrappers and helpers for using Flask with Vector. + +Flask is a Python web framework. remote_control_vector.py and other scripts may use +these utility functions to interact with a web browser. +""" + +import logging +import sys +from threading import Thread +import webbrowser +from time import sleep +from io import BytesIO +try: + from flask import make_response, Response, send_file +except ImportError: + sys.exit("Cannot import from flask: Do `pip3 install --user flask` to install") + + +def _delayed_open_web_browser(url, delay, new=0, autoraise=True, specific_browser=None): + """ + Spawn a thread and call sleep_and_open_web_browser from within it so that main thread can keep executing at the + same time. Insert a small sleep before opening a web-browser + this gives Flask a chance to start running before the browser starts requesting data from Flask. + """ + + def _sleep_and_open_web_browser(url, delay, new, autoraise, specific_browser): + sleep(delay) + browser = webbrowser + + # E.g. On OSX the following would use the Chrome browser app from that location + # specific_browser = 'open -a /Applications/Google\ Chrome.app %s' + if specific_browser: + browser = webbrowser.get(specific_browser) + + browser.open(url, new=new, autoraise=autoraise) + + thread = Thread(target=_sleep_and_open_web_browser, + kwargs=dict(url=url, new=new, autoraise=autoraise, delay=delay, specific_browser=specific_browser)) + thread.daemon = True # Force to quit on main quitting + thread.start() + + +def run_flask(flask_app, host_ip="127.0.0.1", host_port=5000, enable_flask_logging=False, + open_page=True, open_page_delay=1.0): + """ + Run the Flask webserver on specified host and port + optionally also open that same host:port page in your browser to connect + """ + + if not enable_flask_logging: + # disable logging in Flask (it's enabled by default) + log = logging.getLogger('werkzeug') + log.setLevel(logging.ERROR) + + if open_page: + # we add a delay (dispatched in another thread) to open the page so that the flask webserver is open + # before the webpage requests any data + _delayed_open_web_browser("http://" + host_ip + ":" + str(host_port), delay=open_page_delay) + + flask_app.run(host=host_ip, port=host_port, use_evalex=False, threaded=True) + + +def shutdown_flask(request): + func = request.environ.get('werkzeug.server.shutdown') + if func is None: + sys.exit('SDK Shutdown') + func() + + +def stream_video(streaming_function): + return Response(streaming_function(), mimetype='multipart/x-mixed-replace; boundary=frame') + + +def make_uncached_response(in_file): + response = make_response(in_file) + response.headers['Pragma-Directive'] = 'no-cache' + response.headers['Cache-Directive'] = 'no-cache' + response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate' + response.headers['Pragma'] = 'no-cache' + response.headers['Expires'] = '0' + return response + + +def serve_pil_image(pil_img, serve_as_jpeg=False, jpeg_quality=70): + """Convert PIL image to relevant image file and send it""" + img_io = BytesIO() + + if serve_as_jpeg: + pil_img.save(img_io, 'JPEG', quality=jpeg_quality) + img_io.seek(0) + return make_uncached_response(send_file(img_io, mimetype='image/jpeg', + add_etags=False)) + pil_img.save(img_io, 'PNG') + img_io.seek(0) + return make_uncached_response(send_file(img_io, mimetype='image/png', + add_etags=False)) From 5f0661c722d2e2ff4256ff31e34095015c870e9e Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Tue, 8 Jan 2019 22:14:31 -0500 Subject: [PATCH 07/12] Change requirements to be automatically installed by pip --- requirements.txt | 3 --- setup.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 3bf4902..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -anki_vector -flask -PIL \ No newline at end of file diff --git a/setup.py b/setup.py index 9d44d71..d047767 100644 --- a/setup.py +++ b/setup.py @@ -22,5 +22,5 @@ 'vector_controller=vector_controller.__main__:main', ], }, - + install_requires=['anki_vector', 'flask', 'Pillow'], ) \ No newline at end of file From ef7defdce8d6093b44687ab9579617a34f620f23 Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Tue, 8 Jan 2019 22:15:57 -0500 Subject: [PATCH 08/12] Ignore vscode files --- .gitignore | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index efc9a6b..11f475e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,6 @@ dist/ downloads/ eggs/ .eggs/ -lib/ -lib64/ parts/ sdist/ var/ @@ -118,3 +116,6 @@ dmypy.json # pipenv Pipfile Pipfile.lock + +# VSCode +.vscode/ \ No newline at end of file From b51a4dba6bed2c4526eec4ba14c3010320656578 Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Tue, 8 Jan 2019 22:48:35 -0500 Subject: [PATCH 09/12] Resolve a problem where freeplay mode could not be toggled --- setup.cfg | 2 ++ setup.py | 2 ++ tests/vector_controller/controller_test.py | 24 ++++++++++++++++++++++ vector_controller/__init__.py | 1 + vector_controller/controller.py | 8 ++++++-- 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 setup.cfg create mode 100644 tests/vector_controller/controller_test.py create mode 100644 vector_controller/__init__.py diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..9af7e6f --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[aliases] +test=pytest \ No newline at end of file diff --git a/setup.py b/setup.py index d047767..91558c6 100644 --- a/setup.py +++ b/setup.py @@ -23,4 +23,6 @@ ], }, install_requires=['anki_vector', 'flask', 'Pillow'], + setup_requires=['pytest-runner'], + tests_require=['pytest', 'pytest-mock'], ) \ No newline at end of file diff --git a/tests/vector_controller/controller_test.py b/tests/vector_controller/controller_test.py new file mode 100644 index 0000000..b2127c5 --- /dev/null +++ b/tests/vector_controller/controller_test.py @@ -0,0 +1,24 @@ +import vector_controller + +import pytest + +class TestFreePlayMode: + @pytest.fixture + def vector_mock(self, mocker): + vector = mocker.Mock() + vector.anim.anim_list = [] + return vector + + def test_exits_freeplay_mode(self, vector_mock): + remote = vector_controller.RemoteControlVector(vector_mock) + + remote.set_freeplay(enabled=False) + + vector_mock.conn.request_control.assert_called_once() + + def test_enters_freeplay_mode(self, vector_mock): + remote = vector_controller.RemoteControlVector(vector_mock) + + remote.set_freeplay(enabled=True) + + vector_mock.conn.release_control.assert_called_once() diff --git a/vector_controller/__init__.py b/vector_controller/__init__.py new file mode 100644 index 0000000..e1fa6a7 --- /dev/null +++ b/vector_controller/__init__.py @@ -0,0 +1 @@ +from .controller import RemoteControlVector \ No newline at end of file diff --git a/vector_controller/controller.py b/vector_controller/controller.py index 1b4448e..c5b088d 100755 --- a/vector_controller/controller.py +++ b/vector_controller/controller.py @@ -305,6 +305,11 @@ def update_mouse_driving(self): self.vector.motors.set_wheel_motors(l_wheel_speed, r_wheel_speed, l_wheel_speed * 4, r_wheel_speed * 4) + def set_freeplay(self, enabled): + connection = self.vector.conn + + connection.release_control() if enabled else connection.request_control() + def get_anim_sel_drop_down(selectorIndex): html_text = """""" @@ -468,6 +468,12 @@ def handle_index_page(): var hasShift = (e.shiftKey ? 1 : 0) var hasCtrl = (e.ctrlKey ? 1 : 0) var hasAlt = (e.altKey ? 1 : 0) + var isRepeat = (e.repeat ? 1 : 0) + + if (isRepeat) + { + return; + } if (actionType=="keyup") { From 108d106689f13ad65a46e95d3377a21d0f57e0cf Mon Sep 17 00:00:00 2001 From: Kodey Converse Date: Thu, 10 Jan 2019 18:14:54 -0500 Subject: [PATCH 12/12] Add a requirements file --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..41df567 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +anki-vector==0.5.1 +Flask==1.0.2 +Pillow==5.4.1 \ No newline at end of file