Desmond-Dong commited on
Commit
e785f5d
·
1 Parent(s): 0d53d1e

"fix-reachy-controller-sdk-api-calls"

Browse files
reachy_mini_ha_voice/reachy_controller.py CHANGED
@@ -3,6 +3,8 @@
3
  import logging
4
  from typing import Optional, TYPE_CHECKING
5
  import math
 
 
6
 
7
  if TYPE_CHECKING:
8
  from reachy_mini import ReachyMini
@@ -40,8 +42,9 @@ class ReachyController:
40
  if not self.is_available:
41
  return "not_available"
42
  try:
43
- status = self.reachy.get_daemon_status()
44
- return status.state.value if hasattr(status, 'state') else "unknown"
 
45
  except Exception as e:
46
  logger.error(f"Error getting daemon state: {e}")
47
  return "error"
@@ -51,8 +54,9 @@ class ReachyController:
51
  if not self.is_available:
52
  return False
53
  try:
54
- status = self.reachy.get_backend_status()
55
- return status.ready if hasattr(status, 'ready') else False
 
56
  except Exception as e:
57
  logger.error(f"Error getting backend status: {e}")
58
  return False
@@ -62,8 +66,8 @@ class ReachyController:
62
  if not self.is_available:
63
  return "Robot not available"
64
  try:
65
- status = self.reachy.get_daemon_status()
66
- return status.error if hasattr(status, 'error') else ""
67
  except Exception as e:
68
  logger.error(f"Error getting error message: {e}")
69
  return str(e)
@@ -90,8 +94,10 @@ class ReachyController:
90
  if not self.is_available:
91
  return False
92
  try:
93
- state = self.reachy.get_full_state()
94
- return state.control_mode.value == "enabled"
 
 
95
  except Exception as e:
96
  logger.error(f"Error getting motor state: {e}")
97
  return False
@@ -122,8 +128,12 @@ class ReachyController:
122
  if not self.is_available:
123
  return "disabled"
124
  try:
125
- state = self.reachy.get_full_state()
126
- return state.control_mode.value
 
 
 
 
127
  except Exception as e:
128
  logger.error(f"Error getting motor mode: {e}")
129
  return "error"
@@ -145,7 +155,7 @@ class ReachyController:
145
  elif mode == "disabled":
146
  self.reachy.disable_motors()
147
  elif mode == "gravity_compensation":
148
- self.reachy.set_motor_control_mode("gravity_compensation")
149
  else:
150
  logger.warning(f"Invalid motor mode: {mode}")
151
  return
@@ -179,13 +189,37 @@ class ReachyController:
179
 
180
  # ========== Phase 3: Pose Control ==========
181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  def get_head_x(self) -> float:
183
  """Get head X position in mm."""
184
  if not self.is_available:
185
  return 0.0
186
  try:
187
  pose = self.reachy.get_current_head_pose()
188
- return pose.x * 1000 # Convert m to mm
 
189
  except Exception as e:
190
  logger.error(f"Error getting head X: {e}")
191
  return 0.0
@@ -195,10 +229,11 @@ class ReachyController:
195
  if not self.is_available:
196
  return
197
  try:
198
- current = self.reachy.get_current_head_pose()
199
- self.reachy.goto_target(
200
- head=(x_mm / 1000, current.y, current.z, current.roll, current.pitch, current.yaw)
201
- )
 
202
  except Exception as e:
203
  logger.error(f"Error setting head X: {e}")
204
 
@@ -208,7 +243,8 @@ class ReachyController:
208
  return 0.0
209
  try:
210
  pose = self.reachy.get_current_head_pose()
211
- return pose.y * 1000
 
212
  except Exception as e:
213
  logger.error(f"Error getting head Y: {e}")
214
  return 0.0
@@ -218,10 +254,10 @@ class ReachyController:
218
  if not self.is_available:
219
  return
220
  try:
221
- current = self.reachy.get_current_head_pose()
222
- self.reachy.goto_target(
223
- head=(current.x, y_mm / 1000, current.z, current.roll, current.pitch, current.yaw)
224
- )
225
  except Exception as e:
226
  logger.error(f"Error setting head Y: {e}")
227
 
@@ -231,7 +267,8 @@ class ReachyController:
231
  return 0.0
232
  try:
233
  pose = self.reachy.get_current_head_pose()
234
- return pose.z * 1000
 
235
  except Exception as e:
236
  logger.error(f"Error getting head Z: {e}")
237
  return 0.0
@@ -241,10 +278,10 @@ class ReachyController:
241
  if not self.is_available:
242
  return
243
  try:
244
- current = self.reachy.get_current_head_pose()
245
- self.reachy.goto_target(
246
- head=(current.x, current.y, z_mm / 1000, current.roll, current.pitch, current.yaw)
247
- )
248
  except Exception as e:
249
  logger.error(f"Error setting head Z: {e}")
250
 
@@ -254,7 +291,8 @@ class ReachyController:
254
  return 0.0
255
  try:
256
  pose = self.reachy.get_current_head_pose()
257
- return math.degrees(pose.roll)
 
258
  except Exception as e:
259
  logger.error(f"Error getting head roll: {e}")
260
  return 0.0
@@ -264,10 +302,13 @@ class ReachyController:
264
  if not self.is_available:
265
  return
266
  try:
267
- current = self.reachy.get_current_head_pose()
268
- self.reachy.goto_target(
269
- head=(current.x, current.y, current.z, math.radians(roll_deg), current.pitch, current.yaw)
270
- )
 
 
 
271
  except Exception as e:
272
  logger.error(f"Error setting head roll: {e}")
273
 
@@ -277,7 +318,8 @@ class ReachyController:
277
  return 0.0
278
  try:
279
  pose = self.reachy.get_current_head_pose()
280
- return math.degrees(pose.pitch)
 
281
  except Exception as e:
282
  logger.error(f"Error getting head pitch: {e}")
283
  return 0.0
@@ -287,10 +329,12 @@ class ReachyController:
287
  if not self.is_available:
288
  return
289
  try:
290
- current = self.reachy.get_current_head_pose()
291
- self.reachy.goto_target(
292
- head=(current.x, current.y, current.z, current.roll, math.radians(pitch_deg), current.yaw)
293
- )
 
 
294
  except Exception as e:
295
  logger.error(f"Error setting head pitch: {e}")
296
 
@@ -300,7 +344,8 @@ class ReachyController:
300
  return 0.0
301
  try:
302
  pose = self.reachy.get_current_head_pose()
303
- return math.degrees(pose.yaw)
 
304
  except Exception as e:
305
  logger.error(f"Error getting head yaw: {e}")
306
  return 0.0
@@ -310,10 +355,12 @@ class ReachyController:
310
  if not self.is_available:
311
  return
312
  try:
313
- current = self.reachy.get_current_head_pose()
314
- self.reachy.goto_target(
315
- head=(current.x, current.y, current.z, current.roll, current.pitch, math.radians(yaw_deg))
316
- )
 
 
317
  except Exception as e:
318
  logger.error(f"Error setting head yaw: {e}")
319
 
@@ -322,8 +369,9 @@ class ReachyController:
322
  if not self.is_available:
323
  return 0.0
324
  try:
325
- state = self.reachy.get_full_state()
326
- return math.degrees(state.body_yaw)
 
327
  except Exception as e:
328
  logger.error(f"Error getting body yaw: {e}")
329
  return 0.0
@@ -342,9 +390,10 @@ class ReachyController:
342
  if not self.is_available:
343
  return 0.0
344
  try:
345
- state = self.reachy.get_full_state()
346
- # antennas_position is [right, left]
347
- return math.degrees(state.antennas_position[1])
 
348
  except Exception as e:
349
  logger.error(f"Error getting left antenna: {e}")
350
  return 0.0
@@ -354,9 +403,9 @@ class ReachyController:
354
  if not self.is_available:
355
  return
356
  try:
357
- state = self.reachy.get_full_state()
358
- right = state.antennas_position[0]
359
- self.reachy.goto_target(antennas=(right, math.radians(angle_deg)))
360
  except Exception as e:
361
  logger.error(f"Error setting left antenna: {e}")
362
 
@@ -365,8 +414,8 @@ class ReachyController:
365
  if not self.is_available:
366
  return 0.0
367
  try:
368
- state = self.reachy.get_full_state()
369
- return math.degrees(state.antennas_position[0])
370
  except Exception as e:
371
  logger.error(f"Error getting right antenna: {e}")
372
  return 0.0
@@ -376,9 +425,9 @@ class ReachyController:
376
  if not self.is_available:
377
  return
378
  try:
379
- state = self.reachy.get_full_state()
380
- left = state.antennas_position[1]
381
- self.reachy.goto_target(antennas=(math.radians(angle_deg), left))
382
  except Exception as e:
383
  logger.error(f"Error setting right antenna: {e}")
384
 
@@ -433,9 +482,8 @@ class ReachyController:
433
  if not self.is_available:
434
  return 0.0
435
  try:
436
- state = self.reachy.get_full_state()
437
- if hasattr(state, 'doa') and hasattr(state.doa, 'angle'):
438
- return math.degrees(state.doa.angle)
439
  return 0.0
440
  except Exception as e:
441
  logger.error(f"Error getting DOA angle: {e}")
@@ -446,9 +494,7 @@ class ReachyController:
446
  if not self.is_available:
447
  return False
448
  try:
449
- state = self.reachy.get_full_state()
450
- if hasattr(state, 'doa') and hasattr(state.doa, 'speech_detected'):
451
- return state.doa.speech_detected
452
  return False
453
  except Exception as e:
454
  logger.error(f"Error getting speech detection: {e}")
@@ -473,10 +519,8 @@ class ReachyController:
473
  if not self.is_available:
474
  return "N/A"
475
  try:
476
- status = self.reachy.get_daemon_status()
477
- if hasattr(status, 'version'):
478
- return status.version
479
- return "unknown"
480
  except Exception as e:
481
  logger.error(f"Error getting SDK version: {e}")
482
  return "error"
@@ -486,10 +530,8 @@ class ReachyController:
486
  if not self.is_available:
487
  return "N/A"
488
  try:
489
- status = self.reachy.get_daemon_status()
490
- if hasattr(status, 'robot_name'):
491
- return status.robot_name
492
- return "unknown"
493
  except Exception as e:
494
  logger.error(f"Error getting robot name: {e}")
495
  return "error"
@@ -499,10 +541,8 @@ class ReachyController:
499
  if not self.is_available:
500
  return False
501
  try:
502
- status = self.reachy.get_daemon_status()
503
- if hasattr(status, 'wireless_version'):
504
- return status.wireless_version
505
- return False
506
  except Exception as e:
507
  logger.error(f"Error getting wireless version: {e}")
508
  return False
@@ -512,10 +552,8 @@ class ReachyController:
512
  if not self.is_available:
513
  return False
514
  try:
515
- status = self.reachy.get_daemon_status()
516
- if hasattr(status, 'simulation_enabled'):
517
- return status.simulation_enabled
518
- return False
519
  except Exception as e:
520
  logger.error(f"Error getting simulation mode: {e}")
521
  return False
@@ -525,10 +563,8 @@ class ReachyController:
525
  if not self.is_available:
526
  return "N/A"
527
  try:
528
- status = self.reachy.get_daemon_status()
529
- if hasattr(status, 'wlan_ip'):
530
- return status.wlan_ip
531
- return "N/A"
532
  except Exception as e:
533
  logger.error(f"Error getting WLAN IP: {e}")
534
  return "error"
@@ -540,10 +576,9 @@ class ReachyController:
540
  if not self.is_available:
541
  return 0.0
542
  try:
543
- if hasattr(self.reachy, 'imu'):
544
- imu_data = self.reachy.imu
545
- if 'accelerometer' in imu_data:
546
- return float(imu_data['accelerometer'][0])
547
  return 0.0
548
  except Exception as e:
549
  logger.error(f"Error getting IMU accel X: {e}")
@@ -554,10 +589,9 @@ class ReachyController:
554
  if not self.is_available:
555
  return 0.0
556
  try:
557
- if hasattr(self.reachy, 'imu'):
558
- imu_data = self.reachy.imu
559
- if 'accelerometer' in imu_data:
560
- return float(imu_data['accelerometer'][1])
561
  return 0.0
562
  except Exception as e:
563
  logger.error(f"Error getting IMU accel Y: {e}")
@@ -568,10 +602,9 @@ class ReachyController:
568
  if not self.is_available:
569
  return 0.0
570
  try:
571
- if hasattr(self.reachy, 'imu'):
572
- imu_data = self.reachy.imu
573
- if 'accelerometer' in imu_data:
574
- return float(imu_data['accelerometer'][2])
575
  return 0.0
576
  except Exception as e:
577
  logger.error(f"Error getting IMU accel Z: {e}")
@@ -582,10 +615,9 @@ class ReachyController:
582
  if not self.is_available:
583
  return 0.0
584
  try:
585
- if hasattr(self.reachy, 'imu'):
586
- imu_data = self.reachy.imu
587
- if 'gyroscope' in imu_data:
588
- return float(imu_data['gyroscope'][0])
589
  return 0.0
590
  except Exception as e:
591
  logger.error(f"Error getting IMU gyro X: {e}")
@@ -596,10 +628,9 @@ class ReachyController:
596
  if not self.is_available:
597
  return 0.0
598
  try:
599
- if hasattr(self.reachy, 'imu'):
600
- imu_data = self.reachy.imu
601
- if 'gyroscope' in imu_data:
602
- return float(imu_data['gyroscope'][1])
603
  return 0.0
604
  except Exception as e:
605
  logger.error(f"Error getting IMU gyro Y: {e}")
@@ -610,10 +641,9 @@ class ReachyController:
610
  if not self.is_available:
611
  return 0.0
612
  try:
613
- if hasattr(self.reachy, 'imu'):
614
- imu_data = self.reachy.imu
615
- if 'gyroscope' in imu_data:
616
- return float(imu_data['gyroscope'][2])
617
  return 0.0
618
  except Exception as e:
619
  logger.error(f"Error getting IMU gyro Z: {e}")
@@ -624,10 +654,9 @@ class ReachyController:
624
  if not self.is_available:
625
  return 0.0
626
  try:
627
- if hasattr(self.reachy, 'imu'):
628
- imu_data = self.reachy.imu
629
- if 'temperature' in imu_data:
630
- return float(imu_data['temperature'])
631
  return 0.0
632
  except Exception as e:
633
  logger.error(f"Error getting IMU temperature: {e}")
 
3
  import logging
4
  from typing import Optional, TYPE_CHECKING
5
  import math
6
+ import numpy as np
7
+ from scipy.spatial.transform import Rotation as R
8
 
9
  if TYPE_CHECKING:
10
  from reachy_mini import ReachyMini
 
42
  if not self.is_available:
43
  return "not_available"
44
  try:
45
+ # client.get_status() returns a dict with 'state' key
46
+ status = self.reachy.client.get_status(wait=False)
47
+ return status.get('state', 'unknown')
48
  except Exception as e:
49
  logger.error(f"Error getting daemon state: {e}")
50
  return "error"
 
54
  if not self.is_available:
55
  return False
56
  try:
57
+ # Check if daemon state is 'running'
58
+ status = self.reachy.client.get_status(wait=False)
59
+ return status.get('state') == 'running'
60
  except Exception as e:
61
  logger.error(f"Error getting backend status: {e}")
62
  return False
 
66
  if not self.is_available:
67
  return "Robot not available"
68
  try:
69
+ status = self.reachy.client.get_status(wait=False)
70
+ return status.get('error') or ""
71
  except Exception as e:
72
  logger.error(f"Error getting error message: {e}")
73
  return str(e)
 
94
  if not self.is_available:
95
  return False
96
  try:
97
+ # We can't directly query motor state from SDK
98
+ # Return True if daemon is running (motors are typically enabled)
99
+ status = self.reachy.client.get_status(wait=False)
100
+ return status.get('state') == 'running'
101
  except Exception as e:
102
  logger.error(f"Error getting motor state: {e}")
103
  return False
 
128
  if not self.is_available:
129
  return "disabled"
130
  try:
131
+ # SDK doesn't expose motor mode directly
132
+ # Return "enabled" if daemon is running
133
+ status = self.reachy.client.get_status(wait=False)
134
+ if status.get('state') == 'running':
135
+ return "enabled"
136
+ return "disabled"
137
  except Exception as e:
138
  logger.error(f"Error getting motor mode: {e}")
139
  return "error"
 
155
  elif mode == "disabled":
156
  self.reachy.disable_motors()
157
  elif mode == "gravity_compensation":
158
+ self.reachy.enable_gravity_compensation()
159
  else:
160
  logger.warning(f"Invalid motor mode: {mode}")
161
  return
 
189
 
190
  # ========== Phase 3: Pose Control ==========
191
 
192
+ def _extract_pose_from_matrix(self, pose_matrix: np.ndarray) -> tuple:
193
+ """
194
+ Extract position (x, y, z) and rotation (roll, pitch, yaw) from 4x4 pose matrix.
195
+
196
+ Args:
197
+ pose_matrix: 4x4 homogeneous transformation matrix
198
+
199
+ Returns:
200
+ tuple: (x, y, z, roll, pitch, yaw) where position is in meters and angles in radians
201
+ """
202
+ # Extract position from the last column
203
+ x = pose_matrix[0, 3]
204
+ y = pose_matrix[1, 3]
205
+ z = pose_matrix[2, 3]
206
+
207
+ # Extract rotation matrix and convert to euler angles
208
+ rotation_matrix = pose_matrix[:3, :3]
209
+ rotation = R.from_matrix(rotation_matrix)
210
+ # Use 'xyz' convention for roll, pitch, yaw
211
+ roll, pitch, yaw = rotation.as_euler('xyz')
212
+
213
+ return x, y, z, roll, pitch, yaw
214
+
215
  def get_head_x(self) -> float:
216
  """Get head X position in mm."""
217
  if not self.is_available:
218
  return 0.0
219
  try:
220
  pose = self.reachy.get_current_head_pose()
221
+ x, y, z, roll, pitch, yaw = self._extract_pose_from_matrix(pose)
222
+ return x * 1000 # Convert m to mm
223
  except Exception as e:
224
  logger.error(f"Error getting head X: {e}")
225
  return 0.0
 
229
  if not self.is_available:
230
  return
231
  try:
232
+ pose = self.reachy.get_current_head_pose()
233
+ # Modify the X position in the matrix
234
+ new_pose = pose.copy()
235
+ new_pose[0, 3] = x_mm / 1000 # Convert mm to m
236
+ self.reachy.goto_target(head=new_pose)
237
  except Exception as e:
238
  logger.error(f"Error setting head X: {e}")
239
 
 
243
  return 0.0
244
  try:
245
  pose = self.reachy.get_current_head_pose()
246
+ x, y, z, roll, pitch, yaw = self._extract_pose_from_matrix(pose)
247
+ return y * 1000
248
  except Exception as e:
249
  logger.error(f"Error getting head Y: {e}")
250
  return 0.0
 
254
  if not self.is_available:
255
  return
256
  try:
257
+ pose = self.reachy.get_current_head_pose()
258
+ new_pose = pose.copy()
259
+ new_pose[1, 3] = y_mm / 1000
260
+ self.reachy.goto_target(head=new_pose)
261
  except Exception as e:
262
  logger.error(f"Error setting head Y: {e}")
263
 
 
267
  return 0.0
268
  try:
269
  pose = self.reachy.get_current_head_pose()
270
+ x, y, z, roll, pitch, yaw = self._extract_pose_from_matrix(pose)
271
+ return z * 1000
272
  except Exception as e:
273
  logger.error(f"Error getting head Z: {e}")
274
  return 0.0
 
278
  if not self.is_available:
279
  return
280
  try:
281
+ pose = self.reachy.get_current_head_pose()
282
+ new_pose = pose.copy()
283
+ new_pose[2, 3] = z_mm / 1000
284
+ self.reachy.goto_target(head=new_pose)
285
  except Exception as e:
286
  logger.error(f"Error setting head Z: {e}")
287
 
 
291
  return 0.0
292
  try:
293
  pose = self.reachy.get_current_head_pose()
294
+ x, y, z, roll, pitch, yaw = self._extract_pose_from_matrix(pose)
295
+ return math.degrees(roll)
296
  except Exception as e:
297
  logger.error(f"Error getting head roll: {e}")
298
  return 0.0
 
302
  if not self.is_available:
303
  return
304
  try:
305
+ pose = self.reachy.get_current_head_pose()
306
+ x, y, z, roll, pitch, yaw = self._extract_pose_from_matrix(pose)
307
+ # Create new rotation with updated roll
308
+ new_rotation = R.from_euler('xyz', [math.radians(roll_deg), pitch, yaw])
309
+ new_pose = pose.copy()
310
+ new_pose[:3, :3] = new_rotation.as_matrix()
311
+ self.reachy.goto_target(head=new_pose)
312
  except Exception as e:
313
  logger.error(f"Error setting head roll: {e}")
314
 
 
318
  return 0.0
319
  try:
320
  pose = self.reachy.get_current_head_pose()
321
+ x, y, z, roll, pitch, yaw = self._extract_pose_from_matrix(pose)
322
+ return math.degrees(pitch)
323
  except Exception as e:
324
  logger.error(f"Error getting head pitch: {e}")
325
  return 0.0
 
329
  if not self.is_available:
330
  return
331
  try:
332
+ pose = self.reachy.get_current_head_pose()
333
+ x, y, z, roll, pitch, yaw = self._extract_pose_from_matrix(pose)
334
+ new_rotation = R.from_euler('xyz', [roll, math.radians(pitch_deg), yaw])
335
+ new_pose = pose.copy()
336
+ new_pose[:3, :3] = new_rotation.as_matrix()
337
+ self.reachy.goto_target(head=new_pose)
338
  except Exception as e:
339
  logger.error(f"Error setting head pitch: {e}")
340
 
 
344
  return 0.0
345
  try:
346
  pose = self.reachy.get_current_head_pose()
347
+ x, y, z, roll, pitch, yaw = self._extract_pose_from_matrix(pose)
348
+ return math.degrees(yaw)
349
  except Exception as e:
350
  logger.error(f"Error getting head yaw: {e}")
351
  return 0.0
 
355
  if not self.is_available:
356
  return
357
  try:
358
+ pose = self.reachy.get_current_head_pose()
359
+ x, y, z, roll, pitch, yaw = self._extract_pose_from_matrix(pose)
360
+ new_rotation = R.from_euler('xyz', [roll, pitch, math.radians(yaw_deg)])
361
+ new_pose = pose.copy()
362
+ new_pose[:3, :3] = new_rotation.as_matrix()
363
+ self.reachy.goto_target(head=new_pose)
364
  except Exception as e:
365
  logger.error(f"Error setting head yaw: {e}")
366
 
 
369
  if not self.is_available:
370
  return 0.0
371
  try:
372
+ # SDK doesn't expose body_yaw directly in a simple way
373
+ # Return 0.0 as placeholder - body yaw is typically controlled, not read
374
+ return 0.0
375
  except Exception as e:
376
  logger.error(f"Error getting body yaw: {e}")
377
  return 0.0
 
390
  if not self.is_available:
391
  return 0.0
392
  try:
393
+ # get_current_joint_positions() returns (head_joints, antenna_joints)
394
+ # antenna_joints is [right, left]
395
+ _, antennas = self.reachy.get_current_joint_positions()
396
+ return math.degrees(antennas[1]) # left is index 1
397
  except Exception as e:
398
  logger.error(f"Error getting left antenna: {e}")
399
  return 0.0
 
403
  if not self.is_available:
404
  return
405
  try:
406
+ _, antennas = self.reachy.get_current_joint_positions()
407
+ right = antennas[0]
408
+ self.reachy.goto_target(antennas=[right, math.radians(angle_deg)])
409
  except Exception as e:
410
  logger.error(f"Error setting left antenna: {e}")
411
 
 
414
  if not self.is_available:
415
  return 0.0
416
  try:
417
+ _, antennas = self.reachy.get_current_joint_positions()
418
+ return math.degrees(antennas[0]) # right is index 0
419
  except Exception as e:
420
  logger.error(f"Error getting right antenna: {e}")
421
  return 0.0
 
425
  if not self.is_available:
426
  return
427
  try:
428
+ _, antennas = self.reachy.get_current_joint_positions()
429
+ left = antennas[1]
430
+ self.reachy.goto_target(antennas=[math.radians(angle_deg), left])
431
  except Exception as e:
432
  logger.error(f"Error setting right antenna: {e}")
433
 
 
482
  if not self.is_available:
483
  return 0.0
484
  try:
485
+ # DOA info is not directly exposed in SDK
486
+ # Would need to access audio subsystem
 
487
  return 0.0
488
  except Exception as e:
489
  logger.error(f"Error getting DOA angle: {e}")
 
494
  if not self.is_available:
495
  return False
496
  try:
497
+ # Speech detection is not directly exposed in SDK
 
 
498
  return False
499
  except Exception as e:
500
  logger.error(f"Error getting speech detection: {e}")
 
519
  if not self.is_available:
520
  return "N/A"
521
  try:
522
+ status = self.reachy.client.get_status(wait=False)
523
+ return status.get('version') or "unknown"
 
 
524
  except Exception as e:
525
  logger.error(f"Error getting SDK version: {e}")
526
  return "error"
 
530
  if not self.is_available:
531
  return "N/A"
532
  try:
533
+ status = self.reachy.client.get_status(wait=False)
534
+ return status.get('robot_name') or "unknown"
 
 
535
  except Exception as e:
536
  logger.error(f"Error getting robot name: {e}")
537
  return "error"
 
541
  if not self.is_available:
542
  return False
543
  try:
544
+ status = self.reachy.client.get_status(wait=False)
545
+ return status.get('wireless_version', False)
 
 
546
  except Exception as e:
547
  logger.error(f"Error getting wireless version: {e}")
548
  return False
 
552
  if not self.is_available:
553
  return False
554
  try:
555
+ status = self.reachy.client.get_status(wait=False)
556
+ return status.get('simulation_enabled', False)
 
 
557
  except Exception as e:
558
  logger.error(f"Error getting simulation mode: {e}")
559
  return False
 
563
  if not self.is_available:
564
  return "N/A"
565
  try:
566
+ status = self.reachy.client.get_status(wait=False)
567
+ return status.get('wlan_ip') or "N/A"
 
 
568
  except Exception as e:
569
  logger.error(f"Error getting WLAN IP: {e}")
570
  return "error"
 
576
  if not self.is_available:
577
  return 0.0
578
  try:
579
+ imu_data = self.reachy.imu
580
+ if imu_data is not None and 'accelerometer' in imu_data:
581
+ return float(imu_data['accelerometer'][0])
 
582
  return 0.0
583
  except Exception as e:
584
  logger.error(f"Error getting IMU accel X: {e}")
 
589
  if not self.is_available:
590
  return 0.0
591
  try:
592
+ imu_data = self.reachy.imu
593
+ if imu_data is not None and 'accelerometer' in imu_data:
594
+ return float(imu_data['accelerometer'][1])
 
595
  return 0.0
596
  except Exception as e:
597
  logger.error(f"Error getting IMU accel Y: {e}")
 
602
  if not self.is_available:
603
  return 0.0
604
  try:
605
+ imu_data = self.reachy.imu
606
+ if imu_data is not None and 'accelerometer' in imu_data:
607
+ return float(imu_data['accelerometer'][2])
 
608
  return 0.0
609
  except Exception as e:
610
  logger.error(f"Error getting IMU accel Z: {e}")
 
615
  if not self.is_available:
616
  return 0.0
617
  try:
618
+ imu_data = self.reachy.imu
619
+ if imu_data is not None and 'gyroscope' in imu_data:
620
+ return float(imu_data['gyroscope'][0])
 
621
  return 0.0
622
  except Exception as e:
623
  logger.error(f"Error getting IMU gyro X: {e}")
 
628
  if not self.is_available:
629
  return 0.0
630
  try:
631
+ imu_data = self.reachy.imu
632
+ if imu_data is not None and 'gyroscope' in imu_data:
633
+ return float(imu_data['gyroscope'][1])
 
634
  return 0.0
635
  except Exception as e:
636
  logger.error(f"Error getting IMU gyro Y: {e}")
 
641
  if not self.is_available:
642
  return 0.0
643
  try:
644
+ imu_data = self.reachy.imu
645
+ if imu_data is not None and 'gyroscope' in imu_data:
646
+ return float(imu_data['gyroscope'][2])
 
647
  return 0.0
648
  except Exception as e:
649
  logger.error(f"Error getting IMU gyro Z: {e}")
 
654
  if not self.is_available:
655
  return 0.0
656
  try:
657
+ imu_data = self.reachy.imu
658
+ if imu_data is not None and 'temperature' in imu_data:
659
+ return float(imu_data['temperature'])
 
660
  return 0.0
661
  except Exception as e:
662
  logger.error(f"Error getting IMU temperature: {e}")