import  asyncio 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								import  dataclasses 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  json 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  logging 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  os 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  ssl 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								import  subprocess 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								import  pyaudio 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  wave 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  aiohttp  import  web 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  aiohttp  import  ClientSession 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  openpilot . common . basedir  import  BASEDIR 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  openpilot . system . webrtc . webrtcd  import  StreamRequestBody 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  openpilot . common . params  import  Params 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								logger  =  logging . getLogger ( " bodyteleop " ) 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								logging . basicConfig ( level = logging . INFO ) 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								TELEOPDIR  =  f " { BASEDIR } /tools/bodyteleop " 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								WEBRTCD_HOST ,  WEBRTCD_PORT  =  " localhost " ,  5001 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								## UTILS 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								async  def  play_sound ( sound :  str ) : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  SOUNDS  =  { 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    " engage " :  " selfdrive/assets/sounds/engage.wav " , 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    " disengage " :  " selfdrive/assets/sounds/disengage.wav " , 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    " error " :  " selfdrive/assets/sounds/warning_immediate.wav " , 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  } 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  assert  sound  in  SOUNDS 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  chunk  =  5120 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  with  wave . open ( os . path . join ( BASEDIR ,  SOUNDS [ sound ] ) ,  " rb " )  as  wf : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    def  callback ( in_data ,  frame_count ,  time_info ,  status ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      data  =  wf . readframes ( frame_count ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      return  data ,  pyaudio . paContinue 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    p  =  pyaudio . PyAudio ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    stream  =  p . open ( format = p . get_format_from_width ( wf . getsampwidth ( ) ) , 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    channels = wf . getnchannels ( ) , 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    rate = wf . getframerate ( ) , 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    output = True , 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    frames_per_buffer = chunk , 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    stream_callback = callback ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    stream . start_stream ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    while  stream . is_active ( ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      await  asyncio . sleep ( 0 ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    stream . stop_stream ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    stream . close ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    p . terminate ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								## SSL 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  create_ssl_cert ( cert_path :  str ,  key_path :  str ) : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  try : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    proc  =  subprocess . run ( f ' openssl req -x509 -newkey rsa:4096 -nodes -out  { cert_path }  -keyout  { key_path }   \
   
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                          - days  365  - subj  " /C=US/ST=California/O=commaai/OU=comma body " ' , 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								                          capture_output = True ,  shell = True ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    proc . check_returncode ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  except  subprocess . CalledProcessError  as  ex : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    raise  ValueError ( f " Error creating SSL certificate: \n [stdout] \n { proc . stdout . decode ( ) } \n [stderr] \n { proc . stderr . decode ( ) } " )  from  ex 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								def  create_ssl_context ( ) : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  cert_path  =  os . path . join ( TELEOPDIR ,  " cert.pem " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  key_path  =  os . path . join ( TELEOPDIR ,  " key.pem " ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  if  not  os . path . exists ( cert_path )  or  not  os . path . exists ( key_path ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    logger . info ( " Creating certificate... " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    create_ssl_cert ( cert_path ,  key_path ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  else : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    logger . info ( " Certificate exists! " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  ssl_context  =  ssl . SSLContext ( protocol = ssl . PROTOCOL_TLS_SERVER ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  ssl_context . load_cert_chain ( cert_path ,  key_path ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  return  ssl_context 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								## ENDPOINTS 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								async  def  index ( request :  ' web.Request ' ) : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  with  open ( os . path . join ( TELEOPDIR ,  " static " ,  " index.html " ) )  as  f : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    content  =  f . read ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  web . Response ( content_type = " text/html " ,  text = content ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								async  def  ping ( request :  ' web.Request ' ) : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  return  web . Response ( text = " pong " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								async  def  sound ( request :  ' web.Request ' ) : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  params  =  await  request . json ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  sound_to_play  =  params [ " sound " ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  await  play_sound ( sound_to_play ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  return  web . json_response ( { " status " :  " ok " } ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								async  def  offer ( request :  ' web.Request ' ) : 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  params  =  await  request . json ( ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  body  =  StreamRequestBody ( params [ " sdp " ] ,  [ " driver " ] ,  [ " testJoystick " ] ,  [ " carState " ] ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  body_json  =  json . dumps ( dataclasses . asdict ( body ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  logger . info ( " Sending offer to webrtcd... " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  webrtcd_url  =  f " http:// { WEBRTCD_HOST } : { WEBRTCD_PORT } /stream " 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  async  with  ClientSession ( )  as  session ,  session . post ( webrtcd_url ,  data = body_json )  as  resp : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    assert  resp . status  ==  200 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    answer  =  await  resp . json ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  web . json_response ( answer ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								def  main ( ) : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  # Enable joystick debug mode 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  Params ( ) . put_bool ( " JoystickDebugMode " ,  True ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  # App needs to be HTTPS for microphone and audio autoplay to work on the browser 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  ssl_context  =  create_ssl_context ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  app  =  web . Application ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  app . router . add_get ( " / " ,  index ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  app . router . add_get ( " /ping " ,  ping ,  allow_head = True ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  app . router . add_post ( " /offer " ,  offer ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  app . router . add_post ( " /sound " ,  sound ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  app . router . add_static ( ' /static ' ,  os . path . join ( TELEOPDIR ,  ' static ' ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  web . run_app ( app ,  access_log = None ,  host = " 0.0.0.0 " ,  port = 5000 ,  ssl_context = ssl_context ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								if  __name__  ==  " __main__ " : 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  main ( )