# Project export: GrabThisSpot

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 10.0
- Tagline: "GrabThisSpot" Control your doggy robot just with boddy gestures and voice. Forget about annoying buttons and joysticks, AI is gonna take care of everything. GrabThisSpot is all you need 🤖🚀
- Devpost: https://devpost.com/software/grabthisspot
- GitHub: https://github.com/ferriitoo/CalHack-Spot
- Video: https://player.vimeo.com/video/879189320?byline=0&portrait=0&title=0#t=
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

🌟 Our inspiration for "GrabThisSpot" came from a desire to bridge the gap between humans and robots. We envisioned a world where controlling robots wasn't just a technical feat, but a magical experience. Imagine feeling like a wizard, commanding a robot with a simple wave or a flick of the wrist. Our love for innovation and a bit of sci-fi imagination fueled our passion to create this project.

### What it does

🤖 "GrabThisSpot" is your portal to the future of robotics. It allows you to control a Boston Dynamics SPOT robot through intuitive gestures. Raise your hand to make it move forward, lower it to go backward, swing it left or right to steer – it's like controlling the robot with a Jedi's finesse. With AI and computer vision, it recognizes your face and your unique gestures, ensuring it responds to you, and only you. Plus, it calculates the distance between you and the robot for safety.

### How we built it

🔨 We combined cutting-edge hardware, software, and a dash of creativity. We integrated the SPOT robot, programmed the gesture recognition system, and fine-tuned the AI to understand the user's intent. Our team worked tirelessly to ensure a seamless user experience, where technology feels like magic.

### Challenges we ran into

🌦️ We faced challenges like a dog chasing its tail. Fine-tuning the gesture recognition system to ensure accuracy and real-time responsiveness was a tough bone to chew. Dealing with different lighting conditions and user variability added to the complexity. But our determination and teamwork helped us overcome these hurdles.

### Accomplishments we're proud of

🏆 We're ecstatic to have turned our vision into reality. Creating a project that allows anyone to effortlessly control a robot with gestures is a big win for us. We're proud of the seamless integration of hardware and software, as well as our dedication to user safety by implementing distance detection.

### What we learned

🧠 This journey taught us the power of innovation, teamwork, and the joy of creating something that wows people. We deepened our understanding of computer vision, AI, and the immense potential of human-robot interaction.

### What's next

🚀 The journey doesn't end here. We're eager to refine our project further and explore new applications for gesture-controlled robotics. Our vision is to make technology even more accessible, interactive, and fun. Watch out for more magic with "GrabThisSpot" in the future!

## README (from the GitHub repository)

# GrabThisSpot

GrabThisSpot is a Gesture-Based Robot Control which gives you the ability to communicate with robots through natural, intuitive means has the potential to redefine the way we engage with these mechanical counterparts. It embarks on a transformative journey into the realm of human-robot interaction, where hand gestures and movements become the conduit for commanding and controlling the movement of a robot. 

![Image 6](https://github.com/ferriitoo/CalHack-Spot/blob/main/pics/3.png)



## Description

GrabThisSpot is about effortlessly guiding a robot's every move, from navigating complex terrains to performing intricate tasks, simply by using your own body's language. No cumbersome controllers or intricate programming required—just the innate expressiveness of your hands and body. This project is dedicated to realizing this vision, pushing the boundaries of technological innovation, and ultimately enhancing the adaptability and accessibility of robotic systems.

![Image 4](https://github.com/ferriitoo/CalHack-Spot/blob/main/pics/1.png)
![Image 5](https://github.com/ferriitoo/CalHack-Spot/blob/main/pics/2.png)


## Built with

The project is built on the foundation of the SPOT robot from Boston Dynamics, harnessing Python and the Flask web framework for development. Key components include computer vision techniques such as pose estimation (MediaPipe by Google) and facial recognition, facilitated by deep learning models. These technologies allow users to control the SPOT robot through natural hand gestures and movements. The integration of various sensors and robust communication protocols ensures real-time responsiveness and reliability, promising to revolutionize human-robot interaction by making it more intuitive and accessible across diverse applications and industries.

![Image 7](https://github.com/ferriitoo/CalHack-Spot/blob/main/pics/4.png)

### Dependencies

- **SPOT ROBOT[By Boston Dynamics]**: This spot robot was used for the project, can be replaced by any robot
- **Mediapipe**: A Python Library for detecting body posture
- **DeepFace**: A python library to detect and recognize faces 
- **OpenCV**: A Python Library for image analysis
- **Axios**: Used to make HTTP requests to the Flask back-end and external APIs.
- **Material-UI**: A UI framework for creating responsive and visually appealing designs.

To install these dependencies, navigate to the base directory and run the following command:

```bash
pip install requirements.txt
```

## Authors

![Image 1](https://github.com/ferriitoo/CalHack-Spot/blob/main/pics/IMG-4250.jpg)
![Image 2](https://github.com/ferriitoo/CalHack-Spot/blob/main/pics/IMG-4257.jpg)
![Image 3](https://github.com/ferriitoo/CalHack-Spot/blob/main/pics/IMG-4261.jpg)

Contributors names and contact info

Yash Khandelwal
Julen Ferro


## Detected evidence (automated analysis)

Indexed codebase: 16 recognized source files, 1019 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- Flask (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (20 of 20)

```
app.py
basic_scripts/hello_spot.py
basic_scripts/script.py
basic_scripts/spot_scripts.ipynb
haarcascade_frontalface_default.xml
instructions_buffer.txt
julen.py
jump.py
paramiko_test.py
pop.py
proto/basic_command.proto
README.md
requirements.txt
spot_controller.py
static/script.js
static/style.css
templates/index.html
wasd_julen.py
wasd.py
whisper.py
```

### Dependencies

- requirements.txt: pip@install flask

### Recent commits (newest first)

- Pushing the main branch of the official code

## Key source files (fetched from GitHub, selected and truncated for size)

### requirements.txt

```
pip install opencv-python
pip install mediapipe
pip install deepface
pip install flask
```

### app.py

```python
from flask import Flask, render_template, request
import requests




import paramiko
import sys
import time


def execute_command_in_robot(char):

    # Configura la conexión SSH
    host = '192.168.80.3'
    port = 20022
    username = 'spot'
    password = 'Merkleb0t'

    # Crea una instancia SSHClient
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    ssh.connect(host, port, username, password)
    print("Conexión SSH establecida")

    print("Press keys (no need to press Enter) to send to the remote script.")
    print("Press 'q' to quit.")

    # Construye el comando para ejecutar el programa en el robot con el carácter como argumento
    comando = f'python3.6 -u /home/spot/julen_yash/julen.py {char}'

    print(f'CHAR: {char}')
    print('EXECUTED COMMAND:', comando)

    # Ejecuta el comando en the robot
    stdin, stdout, stderr = ssh.exec_command(comando)

    # Wait for the command to complete
    stdout.channel.recv_exit_status()

    # Print the standard output and error
    print("STDOUT:", stdout.read().decode())
    print("STDERR:", stderr.read().decode())

    # Cierra la conexión SSH
    ssh.close()
    print("Conexión SSH cerrada")



app = Flask(__name__)



# YASH
# ----------------------------------------------------------------




import cv2
import mediapipe as mp
import matplotlib.pyplot as plt
import numpy as np
from deepface import DeepFace
import time
import paramiko


# def main():
    
#     cap = cv2.VideoCapture(0)
    
#     while(True):
#         cap.release()
#         cv2.destroyAllWindows()
        
    
    
# ----------------------------------------------------------------------

import cv2
import numpy as np
from flask import Flask, render_template, Response


class VideoCamera(object):
    def __init__(self):
        self.mp_pose = mp.solutions.pose
        self.pose_image = self.mp_pose.Pose(static_image_mode=True, min_detection_confidence=0.5)
        self.pose_video = self.mp_pose.Pose(static_image_mode=False, min_detection_confidence=0.7,min_tracking_confidence=0.7)
        self.mp_drawing = mp.solutions.drawing_utils
        self.action_list = ["left", "right", "backward", "forward", "stop"]
        self.face_detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
        self.isdebug = False
        self.with_authentication = False
        self.use_video_stream = False
        if not self.isdebug:
            time.sleep(1)
            self.execute_command_in_robot("l")
            time.sleep(5)
            self.execute_command_in_robot("P")
            time.sleep(5)
            self.execute_command_in_robot("f")
            time.sleep(1)
        
        self.last_entry = [time.time()]*len(self.action_list)
        self.global_actions = []
        self.buffer = 10
        resolution = (640, 480)
        if self.with_authentication:
            self.owner = cv2.imread("yash.jpg")
            self.owner = cv2.resize(self.owner, resolution)
        if self.use_video_stream:
            self.video = cv2.VideoCapture("http://10.66.31.34:8080/video")
        else:
            self.video = cv2.VideoCapture(0)
        if not (self.video.isOpened()):
            print("Could not open video device")
        self.video.set(cv2.CAP_PROP_FRAME_WIDTH, resolution[0])
        self.video.set(cv2.CAP_PROP_FRAME_HEIGHT, resolution[1])
        self.cnt = 0
        self.prev_result = []
    
    def execute_command_in_robot(self, char):

        # Configura la conexión SSH
        host = '192.168.80.3'
        port = 20022
        username = 'spot'
        password = 'Merkleb0t'

        # Crea una instancia SSHClient
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        ssh.connect(host, port, username, password)
        print("Conexión SSH establecida")

        print("Press keys (no need to press Enter) to send to the remote script.")
        print("Press 'q' to quit.")

        # Construye el comando para ejecutar el programa en el robot con el carácter como argumento
        comando = f'python3.6 -u /home/spot/julen_yash/julen.py {char}'

        print(f'CHAR: {char}')
        print('EXECUTED COMMAND:', comando)

        # Ejecuta el comando en the robot
        stdin, stdout, stderr = ssh.exec_command(comando)

        # Wait for the command to complete
        stdout.channel.recv_exit_status()

        # Print the standard output and error
        print("STDOUT:", stdout.read().decode())
        print("STDERR:", stderr.read().decode())

        # Cierra la conexión SSH
        ssh.close()
        print("Conexión SSH cerrada")
    
    def Distance_finder(self, Focal_Length, real_face_width, face_width_in_frame):
        distance = (real_face_width * Focal_Length) / face_width_in_frame
        return distance

    def face_data(self, image, CallOut, Distance_level):
        face_width = 0
        face_x, face_y = 0, 0
        face_center_x = 0
        face_center_y = 0
        gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        faces = self.face_detector.detectMultiScale(gray_image, 1.3, 5)
        for (x, y, w, h) in faces:
            line_thickness = 2
            # print(len(faces))
            GREEN = (0, 255, 0)
            LLV = int(h * 0.12)

            # print(LLV)

            # cv2.rectangle(image, (x, y), (x+w, y+h), BLACK, 1)
            cv2.line(image, (x, y + LLV), (x + w, y + LLV), (GREEN), line_thickness)
            cv2.line(image, (x, y + h), (x + w, y + h), (GREEN), line_thickness)
            cv2.line(image, (x, y + LLV), (x, y + LLV + LLV), (GREEN), line_thickness)
            cv2.line(
                image, (x + w, y + LLV), (x + w, y + LLV + LLV), (GREEN), line_thickness
            )
            cv2.line(image, (x, y + h), (x, y + h - LLV), (GREEN), line_thickness)
            cv2.line(image, (x + w, y + h), (x + w, y + h - LLV), (GREEN), line_thickness)

            face_width = w
            face_cent
[truncated — 16212 more characters]
```

### jump.py

```python
from bosdyn.client import spot
from bosdyn.client.spot import robot_command_pb2
from bosdyn.client.spot import robot_command
import bosdyn.client

# Create a connection to the robot
sdk = bosdyn.client.create_standard_sdk('MyApp')
robot = sdk.create_robot('192.168.80.3')
robot.authenticate('admin', '2zqa8dgw7lor')

# Parameters for the jump
jump_height = 0.5  # Set the desired jump height in meters
jump_duration = 2.0  # Set the duration of the jump in seconds

# Create a robot command client
robot_command_client = robot.ensure_client(robot_command.Client.name)

# Create a Jump command
jump_cmd = robot_command_pb2.JumpCommandParams()
jump_cmd.height = jump_height
jump_cmd.duration = jump_duration

# Send the jump command to the robot
robot_command_client.power_jump(jump_cmd)

# Disconnect from the robot
robot.power_off()

```

### pop.py

```python
# import time

# instruction_file = '/home/spot/julen_yash/instructions_buffer.txt'

# while True:
#     time.sleep(5)  # Wait for 5 seconds

#     with open(instruction_file, 'r') as file:
#         lines = file.readlines()

#     if lines:
#         # Read the first character
#         char = lines[0].strip()

#         # Update the file with the remaining characters
#         with open(instruction_file, 'w') as file:
#             file.writelines(lines[1:])

#         # Print the message
#         print(f"I popped this char: {char}")
#     else:
#         print("No characters in the file")



def getting_char():

    instruction_file = '/home/spot/julen_yash/instructions_buffer.txt'
    instruction_file = 'instructions_buffer.txt'

    with open(instruction_file, 'r') as file:
        lines = file.readlines()

    if lines:
        # Read the first character
        char = lines[0].strip()

        # Update the file with the remaining characters
        with open(instruction_file, 'w') as file:
            file.writelines(lines[1:])

        # Print the message
        print(f"I popped this char: {char}")
        return char

```

### paramiko_test.py

```python
import paramiko
import sys
import time
import getch  # Import the getch library

# Configura la conexión SSH
host = '192.168.80.3'
port = 20022
username = 'spot'
password = 'Merkleb0t'

# Crea una instancia SSHClient
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

try:
    ssh.connect(host, port, username, password)
    print("Conexión SSH establecida")

    print("Press keys (no need to press Enter) to send to the remote script.")
    print("Press 'q' to quit.")

    while True:
        char = getch.getch()  # Capture individual keypresses
        
        if char == 'q':
            break  # Quit if 'q' is pressed

        # Construye el comando para ejecutar el programa en el robot con el carácter como argumento
        comando = f'python3.6 -u /home/spot/julen_yash/julen.py {char}'

        print(f'CHAR: {char}')
        print('EXECUTED COMMAND:', comando)

        # Ejecuta el comando en the robot
        stdin, stdout, stderr = ssh.exec_command(comando)

        # Wait for the command to complete
        stdout.channel.recv_exit_status()

        # Print the standard output and error
        print("STDOUT:", stdout.read().decode())
        print("STDERR:", stderr.read().decode())

finally:
    # Cierra la conexión SSH
    ssh.close()
    print("Conexión SSH cerrada")

```

### julen.py

```python
import sys

mapping = {
    'w': 'forward',
    's': 'backwards',
    'a': 'left',
    'd': 'right',
    'z': 'cleaning'
}

def main():
    return_message = ''

    if len(sys.argv) != 2:
        return_message += "\nUsage: python char_echo.py <character>"
        return

    char = sys.argv[1]

    if len(char) != 1:
        return_message += "\nPlease enter only one character."
        return

    instruction_file = '/home/spot/julen_yash/instructions_buffer.txt'

    try:
        if char == 'z':
            with open(instruction_file, 'w') as file:
                file.write('')  # This will empty the file
                print('Cleaning the .txt file')
                
        else:
            with open(instruction_file, 'a') as file:
                file.write(char)
                file.write('\n')
                print('')


    except Exception as e:
        return_message += f'\n{e}'

    # Process the character (you can add more logic here if needed)
    result = process_char(char)

    # Print the processed character
    return_message += f"\nProcessed character: {char}.\nProcessed instruction: {result}.\nWritten {char} to {instruction_file}\n"
    print(return_message)
    sys.stdout.flush()

    # Return the processed character as an exit code
    sys.exit(result)

def process_char(char):
    # You can add your processing logic here
    # For now, it just returns the character as is
    global mapping
    return mapping.get(char, "Unknown")

if __name__ == "__main__":
    main()

```

### whisper.py

```python
import pyaudio
import wave

def record_audio(output_file, duration=10, sample_rate=44100, channels=1):
    audio = pyaudio.PyAudio()
    stream = audio.open(format=pyaudio.paInt16, channels=channels,
                        rate=sample_rate, input=True,
                        frames_per_buffer=1024)
    frames = []

    print("Recording audio...")
    for i in range(0, int(sample_rate / 1024 * duration)):
        data = stream.read(1024)
        frames.append(data)

    print("Finished recording.")
    stream.stop_stream()
    stream.close()
    audio.terminate()

    wf = wave.open(output_file, 'wb')
    wf.setnchannels(channels)
    wf.setsampwidth(audio.get_sample_size(pyaudio.paInt16))
    wf.setframerate(sample_rate)
    wf.writeframes(b''.join(frames))
    wf.close()
    



record_audio('output.wav', duration=10)


# Note: you need to be using OpenAI Python v0.27.0 for the code below to work
import openai

openai.api_key = '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'

audio_file= open("output.wav", "rb")

transcript = openai.Audio.transcribe("whisper-1", audio_file)['text']
# transcript = openai.Audio.transcribe("whisper-1", 'output.wav')

print('TRANSCRIPT: ', transcript)


prompt = f"""

Based on the mapping of characters to each of the keyboard movements: 
mapping = '2':'w', '4': 's', '1': 'a', '3':'d'.
You should build a sequence of movements by concatenating different 
characters based on the number of times each movement is mentioned 
in the <TRANSCRIPT> am gonna give you.

For instance: 1, 1, 3, 3, 3, 2, 2, 4
would be mapped into a result like: 'a a d d d w w s'

This is the <TRANSCRIPT>, which is the input to your response: {transcript} 

Just return the sequence of characters after mapping the transcript 
movements into characters. Do not return any programming code.

"""


response = openai.Completion.create(
            engine="text-davinci-002",  # Use the appropriate engine
            prompt=prompt,
            max_tokens=50,  # You can adjust this to control response length
        )

print('RESPONSE: ', response)

print(response['choices'][0])
```

### spot_controller.py

```python
import time
import bosdyn.client
from bosdyn.client.robot_command import RobotCommandClient, RobotCommandBuilder, blocking_stand  # , blocking_sit
from bosdyn.geometry import EulerZXY
from bosdyn.api.spot import robot_command_pb2 as spot_command_pb2
from scipy.interpolate import Rbf
from bosdyn.client.frame_helpers import ODOM_FRAME_NAME
from bosdyn.api.basic_command_pb2 import RobotCommandFeedbackStatus
from bosdyn.client.estop import EstopClient, EstopEndpoint, EstopKeepAlive
from bosdyn.client.robot_state import RobotStateClient
from bosdyn.client.frame_helpers import ODOM_FRAME_NAME, VISION_FRAME_NAME, BODY_FRAME_NAME,\
    GRAV_ALIGNED_BODY_FRAME_NAME, get_se2_a_tform_b
from bosdyn.client import math_helpers


import traceback

from settings.settings import SPOT_IP, SPOT_USERNAME, SPOT_PASSWORD

VELOCITY_CMD_DURATION = 0.5


def get_spot_position():
    sdk = bosdyn.client.create_standard_sdk('ControllingSDK')
    robot = sdk.create_robot(SPOT_IP)
    robot.authenticate(SPOT_USERNAME, SPOT_PASSWORD)

    state_client = robot.ensure_client(RobotStateClient.default_service_name)

    position = state_client.get_robot_state().kinematic_state.transforms_snapshot.child_to_parent_edge_map[
        "gpe"].parent_tform_child.position
    return position


class SpotController:
    def __init__(self, username, password, robot_ip, coord_nodes):
        self.username = username
        self.password = password
        self.robot_ip = robot_ip

        self.coord_nodes = coord_nodes

        sdk = bosdyn.client.create_standard_sdk('ControllingSDK')

        self.robot = sdk.create_robot(robot_ip)
        id_client = self.robot.ensure_client('robot-id')

        self.robot.authenticate(username, password)

        self.command_client = self.robot.ensure_client(RobotCommandClient.default_service_name)
        self.yaw_interpolate = Rbf(coord_nodes["x"], coord_nodes["y"], coord_nodes["yaw"], function="linear")
        self.pitch_interpolate = Rbf(coord_nodes["x"], coord_nodes["y"], coord_nodes["pitch"], function="linear")

        self.robot.logger.info("Authenticated")

        self._lease_client = None
        self._lease = None
        self._lease_keepalive = None

        self._estop_client = self.robot.ensure_client(EstopClient.default_service_name)
        self._estop_endpoint = EstopEndpoint(self._estop_client, 'GNClient', 9.0)
        self._estop_keepalive = None

        self.state_client = self.robot.ensure_client(RobotStateClient.default_service_name)

    def release_estop(self):
        self._estop_endpoint.force_simple_setup()
        self._estop_keepalive = EstopKeepAlive(self._estop_endpoint)

    def set_estop(self):
        if self._estop_keepalive:
            try:
                self._estop_keepalive.stop()
            except:
                self.robot.logger.error("Failed to set estop")
                traceback.print_exc()
            self._estop_keepalive.shutdown()
            self._estop_keepalive = None

    def lease_control(self):
        self._lease_client =  self.robot.ensure_client('lease')
        self._lease = self._lease_client.take()
        self._lease_keepalive = bosdyn.client.lease.LeaseKeepAlive(self._lease_client, must_acquire=True)
        self.robot.logger.info("Lease acquired")

    def return_lease(self):
        self._lease_client.return_lease(self._lease)
        self._lease_keepalive.shutdown()
        self._lease_keepalive = None

    def __enter__(self):
        self.lease_control()
        self.release_estop()
        self.power_on_stand_up()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            self.robot.logger.error("Spot powered off with "+exc_val + " exception")
        self.power_off_sit_down()
        self.return_lease()
        self.set_estop()

        return True if exc_type else False

    def update_interpolator(self, coord_nodes):
        self.coord_nodes = coord_nodes
        self.yaw_interpolate = Rbf(coord_nodes["x"], coord_nodes["y"], coord_nodes["yaw"], function="linear")
        self.pitch_interpolate = Rbf(coord_nodes["x"], coord_nodes["y"], coord_nodes["pitch"], function="linear")

    def move_head_in_points(self, yaws, pitches, rolls, body_height=-0.3, sleep_after_point_reached=0, timeout=3):
        for i in range(len(yaws)):
            footprint_r_body = EulerZXY(yaw=yaws[i], roll=rolls[i], pitch=pitches[i])
            params = RobotCommandBuilder.mobility_params(footprint_R_body=footprint_r_body, body_height=body_height)
            blocking_stand(self.command_client, timeout_sec=timeout, update_frequency=0.02, params=params)
            self.robot.logger.info("Moved to yaw={} rolls={} pitch={}".format(yaws[i], rolls[i], pitches[i]))
            if sleep_after_point_reached:
                time.sleep(sleep_after_point_reached)

    def wait_until_action_complete(self, cmd_id, timeout=15):
        start_time = time.time()
        while time.time() - start_time < timeout:
            feedback = self.command_client.robot_command_feedback(cmd_id)
            mobility_feedback = feedback.feedback.synchronized_feedback.mobility_command_feedback
            if mobility_feedback.status != RobotCommandFeedbackStatus.STATUS_PROCESSING:
                print("Failed to reach the goal")
                return False
            traj_feedback = mobility_feedback.se2_trajectory_feedback
            if (traj_feedback.status == traj_feedback.STATUS_AT_GOAL and
                    traj_feedback.body_movement_status == traj_feedback.BODY_STATUS_SETTLED):
                print("Arrived at the goal.")
                return True
            time.sleep(0.5)

    def move_to_goal(self, goal_x, goal_y):
        cmd = RobotCommandBuilder.synchro_se2_trajectory_point_command(goal_x=goal_x, goal_y=goal_y, goal_heading=0,
                                                                       frame_name=ODOM_FRAME_NAME)
        cmd_id = self.command_client.robot_command(lease=None, command=cmd,
 
[truncated — 4255 more characters]
```

### basic_scripts/script.py

```python
from bosdyn.client import Robot
from bosdyn.client.spot import Spot

# Replace these with your robot's information
# robot_ip = "192.168.50.3"
# username = "spot"
# password = "Merkleb0t"

robot_ip="192.168.50.3"
# ROBOT_IP="localhost:2000"
username = "admin"
password = "2zqa8dgw7lor"



# Create a Robot object
robot = Robot(robot_ip)
robot.authenticate(username, password)

# Create a Spot object for interacting with Spot-specific features
spot = robot.ensure_client(Spot.client_name)

```

### static/style.css

```css
body {
    font-family: Arial, sans-serif;
    text-align: center;
    margin: 20px;
}

h1 {
    color: #333;
}

div {
    margin: 10px;
}

label {
    font-weight: bold;
}

input {
    padding: 5px;
}

button {
    background-color: #007BFF;
    color: white;
    border: none;
    padding: 10px 20px;
    cursor: pointer;
}

#response {
    font-weight: bold;
    color: #007BFF;
    margin: 20px;
}

/* Add your desired styles here */
.icon-record::before {
    content: "\25B6";
}

.icon-stop::before {
    content: "\25A0";
}

```

[5 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]