wirepod-vector-sdk-audio 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. anki_vector/__init__.py +43 -0
  2. anki_vector/animation.py +272 -0
  3. anki_vector/annotate.py +590 -0
  4. anki_vector/audio.py +212 -0
  5. anki_vector/audio_stream.py +335 -0
  6. anki_vector/behavior.py +1135 -0
  7. anki_vector/camera.py +670 -0
  8. anki_vector/camera_viewer/__init__.py +121 -0
  9. anki_vector/color.py +88 -0
  10. anki_vector/configure/__main__.py +331 -0
  11. anki_vector/connection.py +838 -0
  12. anki_vector/events.py +420 -0
  13. anki_vector/exceptions.py +185 -0
  14. anki_vector/faces.py +819 -0
  15. anki_vector/lights.py +210 -0
  16. anki_vector/mdns.py +131 -0
  17. anki_vector/messaging/__init__.py +45 -0
  18. anki_vector/messaging/alexa_pb2.py +36 -0
  19. anki_vector/messaging/alexa_pb2_grpc.py +3 -0
  20. anki_vector/messaging/behavior_pb2.py +40 -0
  21. anki_vector/messaging/behavior_pb2_grpc.py +3 -0
  22. anki_vector/messaging/client.py +33 -0
  23. anki_vector/messaging/cube_pb2.py +113 -0
  24. anki_vector/messaging/cube_pb2_grpc.py +3 -0
  25. anki_vector/messaging/extensions_pb2.py +25 -0
  26. anki_vector/messaging/extensions_pb2_grpc.py +3 -0
  27. anki_vector/messaging/external_interface_pb2.py +169 -0
  28. anki_vector/messaging/external_interface_pb2_grpc.py +1267 -0
  29. anki_vector/messaging/messages_pb2.py +431 -0
  30. anki_vector/messaging/messages_pb2_grpc.py +3 -0
  31. anki_vector/messaging/nav_map_pb2.py +33 -0
  32. anki_vector/messaging/nav_map_pb2_grpc.py +3 -0
  33. anki_vector/messaging/protocol.py +33 -0
  34. anki_vector/messaging/response_status_pb2.py +27 -0
  35. anki_vector/messaging/response_status_pb2_grpc.py +3 -0
  36. anki_vector/messaging/settings_pb2.py +72 -0
  37. anki_vector/messaging/settings_pb2_grpc.py +3 -0
  38. anki_vector/messaging/shared_pb2.py +54 -0
  39. anki_vector/messaging/shared_pb2_grpc.py +3 -0
  40. anki_vector/motors.py +127 -0
  41. anki_vector/nav_map.py +409 -0
  42. anki_vector/objects.py +1782 -0
  43. anki_vector/opengl/__init__.py +103 -0
  44. anki_vector/opengl/assets/LICENSE.txt +21 -0
  45. anki_vector/opengl/assets/cube.jpg +0 -0
  46. anki_vector/opengl/assets/cube.mtl +9 -0
  47. anki_vector/opengl/assets/cube.obj +1000 -0
  48. anki_vector/opengl/assets/vector.mtl +67 -0
  49. anki_vector/opengl/assets/vector.obj +13220 -0
  50. anki_vector/opengl/opengl.py +864 -0
  51. anki_vector/opengl/opengl_vector.py +620 -0
  52. anki_vector/opengl/opengl_viewer.py +689 -0
  53. anki_vector/photos.py +145 -0
  54. anki_vector/proximity.py +176 -0
  55. anki_vector/reserve_control/__main__.py +36 -0
  56. anki_vector/robot.py +930 -0
  57. anki_vector/screen.py +201 -0
  58. anki_vector/status.py +322 -0
  59. anki_vector/touch.py +119 -0
  60. anki_vector/user_intent.py +186 -0
  61. anki_vector/util.py +1132 -0
  62. anki_vector/version.py +15 -0
  63. anki_vector/viewer.py +403 -0
  64. anki_vector/vision.py +202 -0
  65. anki_vector/world.py +899 -0
  66. wirepod_vector_sdk_audio-0.9.0.dist-info/METADATA +80 -0
  67. wirepod_vector_sdk_audio-0.9.0.dist-info/RECORD +71 -0
  68. wirepod_vector_sdk_audio-0.9.0.dist-info/WHEEL +5 -0
  69. wirepod_vector_sdk_audio-0.9.0.dist-info/licenses/LICENSE.txt +180 -0
  70. wirepod_vector_sdk_audio-0.9.0.dist-info/top_level.txt +1 -0
  71. wirepod_vector_sdk_audio-0.9.0.dist-info/zip-safe +1 -0
@@ -0,0 +1,121 @@
1
+ # Copyright (c) 2018 Anki, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License in the file LICENSE.txt or at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """This module provides the camera viewer's render process.
16
+
17
+ It should be launched in a separate process to allow Vector to run freely while
18
+ the viewer is rendering.
19
+
20
+ It uses Tkinter, a standard Python GUI package.
21
+ It also depends on the Pillow library for image processing.
22
+ """
23
+
24
+ import multiprocessing as mp
25
+ import sys
26
+ import tkinter as tk
27
+
28
+ try:
29
+ from PIL import ImageTk
30
+ except ImportError:
31
+ sys.exit('Cannot import from PIL: Do `pip3 install --user "wirepod_vector_sdk[3dviewer]"` to install')
32
+
33
+
34
+ class TkCameraViewer: # pylint: disable=too-few-public-methods
35
+ """A Tkinter based camera video feed.
36
+
37
+ :param queue: A queue to send frames between the user's main thread and the viewer process.
38
+ :param event: An event to signal that the viewer process has closed.
39
+ :param overlays: Overlays to be drawn on the images of the renderer.
40
+ :param timeout: The time without a new frame before the process will exit.
41
+ :param force_on_top: Specifies whether the window should be forced on top of all others.
42
+ """
43
+
44
+ def __init__(self, queue: mp.Queue, event: mp.Event, overlays: list = None, timeout: float = 10.0, force_on_top: bool = True):
45
+ self.tk_root = tk.Tk()
46
+ self.width = None
47
+ self.height = None
48
+ self.queue = queue
49
+ self.event = event
50
+ self.overlays = overlays
51
+ self.timeout = timeout
52
+ self.tk_root.title("Vector Camera Feed")
53
+ self.tk_root.protocol("WM_DELETE_WINDOW", self._delete_window)
54
+ self.tk_root.bind("<Configure>", self._resize_window)
55
+ if force_on_top:
56
+ self.tk_root.wm_attributes("-topmost", 1)
57
+ self.label = tk.Label(self.tk_root, borderwidth=0)
58
+ self.label.pack(fill=tk.BOTH, expand=True)
59
+
60
+ def _delete_window(self) -> None:
61
+ """Handle window close event."""
62
+ self.event.set()
63
+ self.tk_root.destroy()
64
+
65
+ def _resize_window(self, evt: tk.Event) -> None:
66
+ """Handle window resize event.
67
+
68
+ :param evt: A Tkinter window event (keyboard, mouse events, etc).
69
+ """
70
+ self.width = evt.width
71
+ self.height = evt.height
72
+
73
+ def draw_frame(self) -> None:
74
+ """Display an image on to a Tkinter label widget."""
75
+ try:
76
+ image = self.queue.get(True, timeout=self.timeout)
77
+ except:
78
+ return
79
+ self.width, self.height = image.size
80
+ while image:
81
+ if self.event.is_set():
82
+ break
83
+ if self.overlays:
84
+ for overlay in self.overlays:
85
+ overlay.apply_overlay(image)
86
+ if (self.width, self.height) != image.size:
87
+ image = image.resize((self.width, self.height))
88
+ tk_image = ImageTk.PhotoImage(image)
89
+ self.label.config(image=tk_image)
90
+ self.label.image = tk_image
91
+ self.tk_root.update_idletasks()
92
+ self.tk_root.update()
93
+ try:
94
+ image = self.queue.get(True, timeout=self.timeout)
95
+ except:
96
+ return
97
+
98
+
99
+ def main(queue: mp.Queue, event: mp.Event, overlays: list = None, timeout: float = 10.0, force_on_top: bool = False) -> None:
100
+ """Rendering the frames in another process. This allows the UI to have the
101
+ main thread of its process while the user code continues to execute.
102
+
103
+ :param queue: A queue to send frames between the user's main thread and the viewer process.
104
+ :param event: An event to signal that the viewer process has closed.
105
+ :param overlays: Overlays to be drawn on the images of the renderer.
106
+ :param timeout: The time without a new frame before the process will exit.
107
+ :param force_on_top: Specifies whether the window should be forced on top of all others.
108
+ """
109
+
110
+ try:
111
+ tk_viewer = TkCameraViewer(queue, event, overlays, timeout, force_on_top)
112
+ tk_viewer.draw_frame()
113
+ except TimeoutError:
114
+ pass
115
+ except KeyboardInterrupt:
116
+ pass
117
+ finally:
118
+ event.set()
119
+
120
+
121
+ __all__ = ['TkCameraViewer', 'main']
anki_vector/color.py ADDED
@@ -0,0 +1,88 @@
1
+ # Copyright (c) 2018 Anki, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License in the file LICENSE.txt or at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Colors to be used with a light or Vector's screen.
17
+ """
18
+
19
+
20
+ class Color:
21
+ """A Color to be used with a Light or Vector's screen.
22
+
23
+ Either int_color or rgb may be used to specify the actual color.
24
+ Any alpha components (from int_color) are ignored - all colors are fully opaque.
25
+
26
+ :param int_color: A 32 bit value holding the binary RGBA value (where A
27
+ is ignored and forced to be fully opaque).
28
+ :param rgb: A tuple holding the integer values from 0-255 for (reg, green, blue)
29
+ :param name: A name to assign to this color.
30
+ """
31
+
32
+ def __init__(self, int_color: int = None, rgb: tuple = None, name: str = None):
33
+ self.name = name
34
+ self._int_color = 0
35
+ if int_color is not None:
36
+ self._int_color = int_color | 0xff
37
+ elif rgb is not None:
38
+ self._int_color = (rgb[0] << 24) | (rgb[1] << 16) | (rgb[2] << 8) | 0xff
39
+
40
+ @property
41
+ def int_color(self) -> int:
42
+ """The encoded integer value of the color."""
43
+ return self._int_color
44
+
45
+ @property
46
+ def rgb565_bytepair(self):
47
+ """bytes[]: Two bytes representing an int16 color with rgb565 encoding.
48
+
49
+ This format reflects the robot's Screen color range, and performing this
50
+ conversion will reduce network traffic when sending Screen data.
51
+ """
52
+
53
+ red5 = ((self._int_color >> 24) & 0xff) >> 3
54
+ green6 = ((self._int_color >> 16) & 0xff) >> 2
55
+ blue5 = ((self._int_color >> 8) & 0xff) >> 3
56
+
57
+ green3_hi = green6 >> 3
58
+ green3_low = green6 & 0x07
59
+
60
+ int_565_color_lowbyte = (green3_low << 5) | blue5
61
+ int_565_color_highbyte = (red5 << 3) | green3_hi
62
+
63
+ return [int_565_color_highbyte, int_565_color_lowbyte]
64
+
65
+
66
+ #: :class:`Color`: Green color instance.
67
+ green = Color(name="green", int_color=0x00ff00ff)
68
+
69
+ #: :class:`Color`: Red color instance.
70
+ red = Color(name="red", int_color=0xff0000ff)
71
+
72
+ #: :class:`Color`: Blue color instance.
73
+ blue = Color(name="blue", int_color=0x0000ffff)
74
+
75
+ #: :class:`Color`: Cyan color instance.
76
+ cyan = Color(name="cyan", int_color=0x00ffffff)
77
+
78
+ #: :class:`Color`: Magenta color instance.
79
+ magenta = Color(name="magenta", int_color=0xff00ffff)
80
+
81
+ #: :class:`Color`: Yellow color instance.
82
+ yellow = Color(name="yellow", int_color=0xffff00ff)
83
+
84
+ #: :class:`Color`: White color instance.
85
+ white = Color(name="white", int_color=0xffffffff)
86
+
87
+ #: :class:`Color`: Instance representing no color (i.e., lights off).
88
+ off = Color(name="off")
@@ -0,0 +1,331 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ ***Anki Vector Python SDK Setup***
5
+
6
+ Vector requires all requests be authorized by an authenticated Anki user.
7
+
8
+ This script will enable this device to authenticate with your Vector
9
+ robot for use with a Vector Python SDK program.
10
+
11
+ Vector must be powered on and connected on the same network as your
12
+ computer. By running this script, you will be asked to provide your
13
+ Anki account credentials, and the script will download an authentication
14
+ token and cert that will grant you access to the robot and his
15
+ capabilities (such as camera and audio) as well as data stored on the
16
+ robot (such as faces and photos).
17
+
18
+ See the README for more information.
19
+
20
+ Use of Vector and the Vector SDK is subject to Anki's Privacy Policy and Terms and Conditions.
21
+
22
+ https://www.anki.com/en-us/company/privacy
23
+ https://www.anki.com/en-us/company/terms-and-conditions
24
+
25
+ """
26
+
27
+ import argparse
28
+ import configparser
29
+ from getpass import getpass
30
+ import json
31
+ import os
32
+ from pathlib import Path
33
+ import platform
34
+ import re
35
+ import socket
36
+ import sys
37
+
38
+ from cryptography import x509
39
+ from cryptography.hazmat.backends import default_backend
40
+ import grpc
41
+ import requests
42
+ try:
43
+ from termcolor import colored # pylint: disable=import-error
44
+ except: # pylint: disable=bare-except
45
+ def colored(text, color=None, on_color=None, attrs=None): # pylint: disable=unused-argument
46
+ return text
47
+
48
+ import anki_vector
49
+ from anki_vector import messaging
50
+
51
+
52
+ class ApiHandler:
53
+ def __init__(self, headers: dict, url: str):
54
+ self._headers = headers
55
+ self._url = url
56
+
57
+ @property
58
+ def headers(self):
59
+ return self._headers
60
+
61
+ @property
62
+ def url(self):
63
+ return self._url
64
+
65
+
66
+ class Api:
67
+ def __init__(self):
68
+ self._handler = ApiHandler(
69
+ headers={
70
+ 'User-Agent': 'Vector-sdk/{} {}/{}'.format(anki_vector.__version__,
71
+ platform.python_implementation(),
72
+ platform.python_version()),
73
+
74
+ 'Anki-App-Key': 'aung2ieCho3aiph7Een3Ei'
75
+ },
76
+ url='https://accounts.api.anki.com/1/sessions'
77
+ )
78
+
79
+ @property
80
+ def name(self):
81
+ return "Anki Cloud"
82
+
83
+ @property
84
+ def handler(self):
85
+ return self._handler
86
+
87
+
88
+ def get_serial(serial=None):
89
+ if not serial:
90
+ serial = os.environ.get('ANKI_ROBOT_SERIAL')
91
+ if not serial:
92
+ print("\n\nPlease find your robot serial number (ex. 00e20100) located on the underside of Vector, or accessible from Vector's debug screen.")
93
+ serial = input('Enter robot serial number: ')
94
+ else:
95
+ print("Found robot serial number in environment variable '{}'".format(colored("ANKI_ROBOT_SERIAL", "green")))
96
+ serial = serial.lower()
97
+ print("Using robot serial number: {}".format(colored(serial, "cyan")))
98
+ return serial
99
+
100
+
101
+ def get_cert(serial=None):
102
+ print("\n\nEnter the IP address and webserver port of your wire-pod instance (ex. 192.168.1.50:8080) (:8080 is the default port)\nLeave this blank and press enter if you want this script to attempt to automatically connect to your wire-pod instance via escapepod.local.")
103
+ podip = input("Enter wire-pod ip: ")
104
+ if podip == "":
105
+ podip = "escapepod.local:8080"
106
+ serial = get_serial(serial)
107
+ print("\nDownloading Vector certificate from wire-pod...", end="")
108
+ sys.stdout.flush()
109
+ r = requests.get('http://{}/session-certs/{}'.format(podip, serial))
110
+ if r.status_code != 200:
111
+ print(colored(" ERROR", "red"))
112
+ sys.exit(r.content)
113
+ print(colored(" DONE", "green"))
114
+ cert = r.content
115
+ return cert, serial
116
+
117
+
118
+ def user_authentication(session_id: bytes, cert: bytes, ip: str, name: str) -> str:
119
+ # Pin the robot certificate for opening the channel
120
+ creds = grpc.ssl_channel_credentials(root_certificates=cert)
121
+
122
+ print("Attempting to download guid from {} at {}:443...".format(colored(name, "cyan"), colored(ip, "cyan")), end="")
123
+ sys.stdout.flush()
124
+ channel = grpc.secure_channel("{}:443".format(ip), creds,
125
+ options=(("grpc.ssl_target_name_override", name,),))
126
+
127
+ # Verify the connection to Vector is able to be established (client-side)
128
+ try:
129
+ # Explicitly grab _channel._channel to test the underlying grpc channel directly
130
+ grpc.channel_ready_future(channel).result(timeout=15)
131
+ except grpc.FutureTimeoutError:
132
+ print(colored(" ERROR", "red"))
133
+ sys.exit("\nUnable to connect to Vector\n"
134
+ "Please be sure to connect via the Vector companion app first, and connect your computer to the same network as your Vector.")
135
+
136
+ try:
137
+ interface = messaging.client.ExternalInterfaceStub(channel)
138
+ request = messaging.protocol.UserAuthenticationRequest(
139
+ user_session_id=session_id.encode('utf-8'),
140
+ client_name=socket.gethostname().encode('utf-8'))
141
+ response = interface.UserAuthentication(request)
142
+ if response.code != messaging.protocol.UserAuthenticationResponse.AUTHORIZED: # pylint: disable=no-member
143
+ print(colored(" ERROR", "red"))
144
+ sys.exit("\nFailed to authorize request:\n"
145
+ "Please be sure to first set up Vector using the companion app.")
146
+ except grpc.RpcError as e:
147
+ print(colored(" ERROR", "red"))
148
+ sys.exit("\nFailed to authorize request:\n"
149
+ "An unknown error occurred '{}'".format(e))
150
+
151
+ print(colored(" DONE\n", "green"))
152
+ return response.client_token_guid
153
+
154
+
155
+ def get_session_token(api, username=None):
156
+ print("Enter your email and password. Make sure to use the same account that was used to set up your Vector.")
157
+ if not username:
158
+ username = input("Enter Email: ")
159
+ else:
160
+ print("Using email from command line: {}".format(colored(username, "cyan")))
161
+ password = getpass("Enter Password: ")
162
+ payload = {'username': username, 'password': password}
163
+
164
+ print("\nAuthenticating with {}...".format(api.name), end="")
165
+ sys.stdout.flush()
166
+ r = requests.post(api.handler.url, data=payload, headers=api.handler.headers)
167
+ if r.status_code != 200:
168
+ print(colored(" ERROR", "red"))
169
+ sys.exit(r.content)
170
+ print(colored(" DONE\n", "green"))
171
+ return json.loads(r.content)
172
+
173
+
174
+ def standardize_name(robot_name):
175
+ # Extend the name if not enough is provided
176
+ if len(robot_name) == 4:
177
+ robot_name = "Vector-{}".format(robot_name.upper())
178
+ # Fix possible capitalization and space/dash/etc.
179
+ if re.match("[Vv]ector.[A-Za-z0-9]{4}", robot_name):
180
+ robot_name = "V{}-{}".format(robot_name[1:-5], robot_name[-4:].upper())
181
+ # Check that the end is valid
182
+ if re.match("Vector-[A-Z0-9]{4}", robot_name):
183
+ return robot_name
184
+ print(colored(" ERROR", "red"))
185
+ sys.exit("Invalid robot name. Please match the format exactly. Example: Vector-A1B2")
186
+
187
+
188
+ def get_name_and_ip(robot_name=None, ip=None):
189
+ if not robot_name:
190
+ robot_name = os.environ.get('VECTOR_ROBOT_NAME')
191
+ if not robot_name:
192
+ print("\n\nFind your robot name (ex. Vector-A1B2) by placing Vector on the charger and double-clicking Vector's backpack button.")
193
+ robot_name = input("Enter robot name: ")
194
+ else:
195
+ print("Found robot name in environment variable '{}'".format(colored("VECTOR_ROBOT_NAME", "green")))
196
+ robot_name = standardize_name(robot_name)
197
+ print("Using robot name: {}".format(colored(robot_name, "cyan")))
198
+ if not ip:
199
+ ip = os.environ.get('ANKI_ROBOT_HOST')
200
+ if not ip:
201
+ 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"
202
+ "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")))
203
+ ip = input("Enter robot ip: ")
204
+ else:
205
+ print("Found robot ip address in environment variable '{}'".format(colored("ANKI_ROBOT_HOST", "green")))
206
+ print("Using IP: {}".format(colored(ip, "cyan")))
207
+ return robot_name, ip
208
+
209
+
210
+ def save_cert(cert, name, serial, anki_dir):
211
+ """Write Vector's certificate to a file located in the user's home directory"""
212
+ os.makedirs(str(anki_dir), exist_ok=True)
213
+ cert_file = str(anki_dir / "{name}-{serial}.cert".format(name=name, serial=serial))
214
+ print("Writing certificate file to '{}'...\n".format(colored(cert_file, "cyan")))
215
+ with os.fdopen(os.open(cert_file, os.O_WRONLY | os.O_CREAT, 0o600), 'wb') as f:
216
+ f.write(cert)
217
+ return cert_file
218
+
219
+
220
+ def validate_cert_name(cert_file, robot_name):
221
+ """Validate the name on Vector's certificate against the user-provided name"""
222
+ with open(cert_file, "rb") as f:
223
+ cert_file = f.read()
224
+ cert = x509.load_pem_x509_certificate(cert_file, default_backend())
225
+ for fields in cert.subject:
226
+ current = str(fields.oid)
227
+ if "commonName" in current:
228
+ common_name = fields.value
229
+ if common_name != robot_name:
230
+ print(colored(" ERROR", "red"))
231
+ sys.exit("The name of the certificate ({}) does not match the name provided ({}).\n"
232
+ "Please verify the name, and try again.".format(common_name, robot_name))
233
+ else:
234
+ return
235
+
236
+
237
+ def write_config(serial, cert_file=None, ip=None, name=None, guid=None, clear=True):
238
+ home = Path.home()
239
+ config_file = str(home / ".anki_vector" / "sdk_config.ini")
240
+ print("Writing config file to '{}'...".format(colored(config_file, "cyan")))
241
+
242
+ config = configparser.ConfigParser(strict=False)
243
+
244
+ try:
245
+ config.read(config_file)
246
+ except configparser.ParsingError:
247
+ if os.path.exists(config_file):
248
+ os.rename(config_file, config_file + "-error")
249
+ if clear:
250
+ config[serial] = {}
251
+ if cert_file:
252
+ config[serial]["cert"] = cert_file
253
+ if ip:
254
+ config[serial]["ip"] = ip
255
+ if name:
256
+ config[serial]["name"] = name
257
+ if guid:
258
+ config[serial]["guid"] = guid.decode("utf-8")
259
+ temp_file = config_file + "-temp"
260
+ if os.path.exists(config_file):
261
+ os.rename(config_file, temp_file)
262
+ try:
263
+ with os.fdopen(os.open(config_file, os.O_WRONLY | os.O_CREAT, 0o600), 'w') as f:
264
+ config.write(f)
265
+ except Exception as e:
266
+ if os.path.exists(temp_file):
267
+ os.rename(temp_file, config_file)
268
+ raise e
269
+ else:
270
+ if os.path.exists(temp_file):
271
+ os.remove(temp_file)
272
+
273
+
274
+ def main(api):
275
+ parser = argparse.ArgumentParser(description=("Vector requires all requests be authorized by an authenticated Anki user. "
276
+ "This script will enable this device to authenticate with your Vector "
277
+ "robot for use with a Vector Python SDK program."),
278
+ epilog=("See the README for more information. "
279
+ "Use of Vector and the Vector SDK is subject to Anki's Privacy Policy and Terms and Conditions. "
280
+ "https://www.anki.com/en-us/company/privacy and "
281
+ "https://www.anki.com/en-us/company/terms-and-conditions"))
282
+ parser.add_argument("-e", "--email", help="The email used by your Anki account.")
283
+ parser.add_argument("-i", "--ip", help=("Your robot ip address (ex. 192.168.42.42). "
284
+ "It may be found by placing Vector on the charger, "
285
+ "double-clicking Vector's backpack button, "
286
+ "then raising and lowering his arms. "
287
+ "If you see {} on his face, "
288
+ "reconnect Vector to your WiFi using the Vector Companion App.".format(colored("XX.XX.XX.XX", "red"))))
289
+ parser.add_argument("-n", "--name", help=("Your robot name (ex. Vector-A1B2). "
290
+ "It may be found by placing Vector on the charger and double-clicking Vector's backpack button."))
291
+ parser.add_argument("-s", "--serial", help=("Your robot serial number (ex. 00e20100). "
292
+ "It is located on the underside of Vector, or accessible from Vector's debug screen."))
293
+ parser.add_argument("-u", "--update", dest="new_ip", help=("Update the stored ip for Vector. This makes it easier to transfer between networks."))
294
+ args = parser.parse_args()
295
+
296
+ if args.new_ip:
297
+ serial = get_serial(args.serial)
298
+ write_config(serial, ip=args.new_ip, clear=False)
299
+ print(colored("\nIP Updated!", "green"))
300
+ sys.exit()
301
+
302
+ print(__doc__)
303
+
304
+ valid = ["y", "Y", "yes", "YES"]
305
+ environ = input("Do you wish to proceed? (y/n) ")
306
+ if environ not in valid:
307
+ sys.exit("Stopping...")
308
+
309
+ name, ip = get_name_and_ip(args.name, args.ip)
310
+ cert, serial = get_cert(args.serial)
311
+
312
+ home = Path.home()
313
+ anki_dir = home / ".anki_vector"
314
+
315
+ cert_file = save_cert(cert, name, serial, anki_dir)
316
+ validate_cert_name(cert_file, name)
317
+
318
+ # token = get_session_token(api, args.email)
319
+ # if not token.get("session"):
320
+ # sys.exit("Session error: {}".format(token))
321
+ token = "2vMhFgktH3Jrbemm2WHkfGN"
322
+
323
+ guid = user_authentication(token, cert, ip, name)
324
+
325
+ # Store credentials in the .anki_vector directory's sdk_config.ini file
326
+ write_config(serial, cert_file, ip, name, guid)
327
+ print(colored("\nSUCCESS!", "green"))
328
+
329
+
330
+ if __name__ == "__main__":
331
+ main(Api())