uuid and commands
Browse files- index.html +90 -18
    	
        index.html
    CHANGED
    
    | @@ -4,6 +4,7 @@ | |
| 4 | 
             
            <head>
         | 
| 5 | 
             
            	<meta charset="UTF-8">
         | 
| 6 | 
             
            	<meta name="viewport" content="width=device-width, initial-scale=1.0">
         | 
|  | |
| 7 | 
             
            	<title>ReachyMini Controller</title>
         | 
| 8 | 
             
            	<style>
         | 
| 9 | 
             
            		* {
         | 
| @@ -121,6 +122,14 @@ | |
| 121 | 
             
            			transform: none;
         | 
| 122 | 
             
            		}
         | 
| 123 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 124 | 
             
            		.log {
         | 
| 125 | 
             
            			margin-top: 30px;
         | 
| 126 | 
             
            			padding: 15px;
         | 
| @@ -149,6 +158,21 @@ | |
| 149 | 
             
            			color: #3c3;
         | 
| 150 | 
             
            		}
         | 
| 151 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 152 | 
             
            		.note {
         | 
| 153 | 
             
            			margin-top: 20px;
         | 
| 154 | 
             
            			padding: 15px;
         | 
| @@ -174,29 +198,37 @@ | |
| 174 | 
             
            		</button>
         | 
| 175 |  | 
| 176 | 
             
            		<div class="commands">
         | 
| 177 | 
            -
            			<button class="command-btn" data-command=" | 
| 178 | 
            -
            			<button class="command-btn" data-command=" | 
| 179 | 
            -
            			<button class="command-btn" data-command=" | 
| 180 | 
            -
            			<button class="command-btn" data-command=" | 
| 181 | 
            -
            			<button class="command-btn" data-command=" | 
| 182 | 
            -
            			<button class="command-btn" data-command="wave" disabled>Wave</button>
         | 
| 183 | 
             
            		</div>
         | 
| 184 |  | 
|  | |
|  | |
| 185 | 
             
            		<div class="log" id="log"></div>
         | 
| 186 |  | 
| 187 | 
             
            		<div class="note">
         | 
| 188 | 
            -
            			<strong>Note:</strong>  | 
| 189 | 
            -
            			 | 
| 190 | 
             
            		</div>
         | 
| 191 | 
             
            	</div>
         | 
| 192 |  | 
| 193 | 
             
            	<script>
         | 
| 194 | 
             
            		let device = null;
         | 
| 195 | 
            -
            		let  | 
|  | |
| 196 | 
             
            		const statusEl = document.getElementById('status');
         | 
| 197 | 
             
            		const connectBtn = document.getElementById('connectBtn');
         | 
| 198 | 
             
            		const commandBtns = document.querySelectorAll('.command-btn');
         | 
| 199 | 
             
            		const logEl = document.getElementById('log');
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 200 |  | 
| 201 | 
             
            		// Check if Web Bluetooth is supported
         | 
| 202 | 
             
            		if (!navigator.bluetooth) {
         | 
| @@ -219,6 +251,14 @@ | |
| 219 | 
             
            			statusEl.className = `status ${state}`;
         | 
| 220 | 
             
            		}
         | 
| 221 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 222 | 
             
            		async function connectToDevice() {
         | 
| 223 | 
             
            			try {
         | 
| 224 | 
             
            				updateStatus('Scanning for devices...', 'connecting');
         | 
| @@ -226,7 +266,7 @@ | |
| 226 |  | 
| 227 | 
             
            				device = await navigator.bluetooth.requestDevice({
         | 
| 228 | 
             
            					filters: [{ name: 'ReachyMini' }],
         | 
| 229 | 
            -
            					optionalServices: [ | 
| 230 | 
             
            				});
         | 
| 231 |  | 
| 232 | 
             
            				addLog(`Found device: ${device.name}`);
         | 
| @@ -235,13 +275,22 @@ | |
| 235 | 
             
            				const server = await device.gatt.connect();
         | 
| 236 | 
             
            				addLog('Connected to GATT server');
         | 
| 237 |  | 
| 238 | 
            -
            				// Get the service | 
| 239 | 
            -
            				const service = await server.getPrimaryService( | 
| 240 | 
             
            				addLog('Got service');
         | 
| 241 |  | 
| 242 | 
            -
            				// Get the characteristic ( | 
| 243 | 
            -
            				 | 
| 244 | 
            -
            				addLog('Got characteristic');
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 245 |  | 
| 246 | 
             
            				updateStatus('Connected to ReachyMini', 'connected');
         | 
| 247 | 
             
            				addLog('Successfully connected!', 'success');
         | 
| @@ -258,12 +307,21 @@ | |
| 258 | 
             
            			}
         | 
| 259 | 
             
            		}
         | 
| 260 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 261 | 
             
            		function onDisconnected() {
         | 
| 262 | 
             
            			updateStatus('Disconnected', 'disconnected');
         | 
| 263 | 
             
            			addLog('Device disconnected', 'error');
         | 
| 264 | 
             
            			connectBtn.textContent = 'Connect to ReachyMini';
         | 
| 265 | 
             
            			commandBtns.forEach(btn => btn.disabled = true);
         | 
| 266 | 
            -
            			 | 
|  | |
| 267 | 
             
            			device = null;
         | 
| 268 | 
             
            		}
         | 
| 269 |  | 
| @@ -275,7 +333,7 @@ | |
| 275 | 
             
            		}
         | 
| 276 |  | 
| 277 | 
             
            		async function sendCommand(command) {
         | 
| 278 | 
            -
            			if (! | 
| 279 | 
             
            				addLog('Not connected to device', 'error');
         | 
| 280 | 
             
            				return;
         | 
| 281 | 
             
            			}
         | 
| @@ -283,8 +341,22 @@ | |
| 283 | 
             
            			try {
         | 
| 284 | 
             
            				const encoder = new TextEncoder();
         | 
| 285 | 
             
            				const data = encoder.encode(command);
         | 
| 286 | 
            -
            				await  | 
| 287 | 
             
            				addLog(`Sent command: ${command}`, 'success');
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 288 | 
             
            			} catch (error) {
         | 
| 289 | 
             
            				addLog(`Failed to send command: ${error.message}`, 'error');
         | 
| 290 | 
             
            				console.error('Send error:', error);
         | 
|  | |
| 4 | 
             
            <head>
         | 
| 5 | 
             
            	<meta charset="UTF-8">
         | 
| 6 | 
             
            	<meta name="viewport" content="width=device-width, initial-scale=1.0">
         | 
| 7 | 
            +
            	<meta http-equiv="Permissions-Policy" content="bluetooth=*">
         | 
| 8 | 
             
            	<title>ReachyMini Controller</title>
         | 
| 9 | 
             
            	<style>
         | 
| 10 | 
             
            		* {
         | 
|  | |
| 122 | 
             
            			transform: none;
         | 
| 123 | 
             
            		}
         | 
| 124 |  | 
| 125 | 
            +
            		.command-btn.danger {
         | 
| 126 | 
            +
            			background: linear-gradient(135deg, #ee5a6f 0%, #f29263 100%);
         | 
| 127 | 
            +
            		}
         | 
| 128 | 
            +
             | 
| 129 | 
            +
            		.command-btn.warning {
         | 
| 130 | 
            +
            			background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
         | 
| 131 | 
            +
            		}
         | 
| 132 | 
            +
             | 
| 133 | 
             
            		.log {
         | 
| 134 | 
             
            			margin-top: 30px;
         | 
| 135 | 
             
            			padding: 15px;
         | 
|  | |
| 158 | 
             
            			color: #3c3;
         | 
| 159 | 
             
            		}
         | 
| 160 |  | 
| 161 | 
            +
            		.response-box {
         | 
| 162 | 
            +
            			margin-top: 20px;
         | 
| 163 | 
            +
            			padding: 15px;
         | 
| 164 | 
            +
            			background: #e7f3ff;
         | 
| 165 | 
            +
            			border-left: 4px solid #2196F3;
         | 
| 166 | 
            +
            			border-radius: 4px;
         | 
| 167 | 
            +
            			font-size: 14px;
         | 
| 168 | 
            +
            			font-family: 'Courier New', monospace;
         | 
| 169 | 
            +
            			display: none;
         | 
| 170 | 
            +
            		}
         | 
| 171 | 
            +
             | 
| 172 | 
            +
            		.response-box.show {
         | 
| 173 | 
            +
            			display: block;
         | 
| 174 | 
            +
            		}
         | 
| 175 | 
            +
             | 
| 176 | 
             
            		.note {
         | 
| 177 | 
             
            			margin-top: 20px;
         | 
| 178 | 
             
            			padding: 15px;
         | 
|  | |
| 198 | 
             
            		</button>
         | 
| 199 |  | 
| 200 | 
             
            		<div class="commands">
         | 
| 201 | 
            +
            			<button class="command-btn" data-command="PING" disabled>Ping</button>
         | 
| 202 | 
            +
            			<button class="command-btn" data-command="STATUS" disabled>Status</button>
         | 
| 203 | 
            +
            			<button class="command-btn" data-command="CMD_HOTSPOT" disabled>Hotspot</button>
         | 
| 204 | 
            +
            			<button class="command-btn danger" data-command="CMD_RESTART_DAEMON" disabled>Restart Daemon</button>
         | 
| 205 | 
            +
            			<button class="command-btn warning" data-command="CMD_SOFTWARE_RESET" disabled>Software Reset</button>
         | 
|  | |
| 206 | 
             
            		</div>
         | 
| 207 |  | 
| 208 | 
            +
            		<div id="responseBox" class="response-box"></div>
         | 
| 209 | 
            +
             | 
| 210 | 
             
            		<div class="log" id="log"></div>
         | 
| 211 |  | 
| 212 | 
             
            		<div class="note">
         | 
| 213 | 
            +
            			<strong>Note:</strong> This requires HTTPS and a Chromium-based browser with Web Bluetooth enabled. Access
         | 
| 214 | 
            +
            			from your phone's hotspot for best results.
         | 
| 215 | 
             
            		</div>
         | 
| 216 | 
             
            	</div>
         | 
| 217 |  | 
| 218 | 
             
            	<script>
         | 
| 219 | 
             
            		let device = null;
         | 
| 220 | 
            +
            		let commandCharacteristic = null;
         | 
| 221 | 
            +
            		let responseCharacteristic = null;
         | 
| 222 | 
             
            		const statusEl = document.getElementById('status');
         | 
| 223 | 
             
            		const connectBtn = document.getElementById('connectBtn');
         | 
| 224 | 
             
            		const commandBtns = document.querySelectorAll('.command-btn');
         | 
| 225 | 
             
            		const logEl = document.getElementById('log');
         | 
| 226 | 
            +
            		const responseBox = document.getElementById('responseBox');
         | 
| 227 | 
            +
             | 
| 228 | 
            +
            		// Correct UUIDs from your server
         | 
| 229 | 
            +
            		const SERVICE_UUID = '12345678-1234-5678-1234-56789abcdef0';
         | 
| 230 | 
            +
            		const COMMAND_CHAR_UUID = '12345678-1234-5678-1234-56789abcdef1';
         | 
| 231 | 
            +
            		const RESPONSE_CHAR_UUID = '12345678-1234-5678-1234-56789abcdef2';
         | 
| 232 |  | 
| 233 | 
             
            		// Check if Web Bluetooth is supported
         | 
| 234 | 
             
            		if (!navigator.bluetooth) {
         | 
|  | |
| 251 | 
             
            			statusEl.className = `status ${state}`;
         | 
| 252 | 
             
            		}
         | 
| 253 |  | 
| 254 | 
            +
            		function showResponse(message) {
         | 
| 255 | 
            +
            			responseBox.textContent = `Response: ${message}`;
         | 
| 256 | 
            +
            			responseBox.classList.add('show');
         | 
| 257 | 
            +
            			setTimeout(() => {
         | 
| 258 | 
            +
            				responseBox.classList.remove('show');
         | 
| 259 | 
            +
            			}, 5000);
         | 
| 260 | 
            +
            		}
         | 
| 261 | 
            +
             | 
| 262 | 
             
            		async function connectToDevice() {
         | 
| 263 | 
             
            			try {
         | 
| 264 | 
             
            				updateStatus('Scanning for devices...', 'connecting');
         | 
|  | |
| 266 |  | 
| 267 | 
             
            				device = await navigator.bluetooth.requestDevice({
         | 
| 268 | 
             
            					filters: [{ name: 'ReachyMini' }],
         | 
| 269 | 
            +
            					optionalServices: [SERVICE_UUID]
         | 
| 270 | 
             
            				});
         | 
| 271 |  | 
| 272 | 
             
            				addLog(`Found device: ${device.name}`);
         | 
|  | |
| 275 | 
             
            				const server = await device.gatt.connect();
         | 
| 276 | 
             
            				addLog('Connected to GATT server');
         | 
| 277 |  | 
| 278 | 
            +
            				// Get the service
         | 
| 279 | 
            +
            				const service = await server.getPrimaryService(SERVICE_UUID);
         | 
| 280 | 
             
            				addLog('Got service');
         | 
| 281 |  | 
| 282 | 
            +
            				// Get the command characteristic (write)
         | 
| 283 | 
            +
            				commandCharacteristic = await service.getCharacteristic(COMMAND_CHAR_UUID);
         | 
| 284 | 
            +
            				addLog('Got command characteristic');
         | 
| 285 | 
            +
             | 
| 286 | 
            +
            				// Get the response characteristic (read/notify)
         | 
| 287 | 
            +
            				responseCharacteristic = await service.getCharacteristic(RESPONSE_CHAR_UUID);
         | 
| 288 | 
            +
            				addLog('Got response characteristic');
         | 
| 289 | 
            +
             | 
| 290 | 
            +
            				// Enable notifications for responses
         | 
| 291 | 
            +
            				await responseCharacteristic.startNotifications();
         | 
| 292 | 
            +
            				responseCharacteristic.addEventListener('characteristicvaluechanged', handleResponse);
         | 
| 293 | 
            +
            				addLog('Notifications enabled for responses');
         | 
| 294 |  | 
| 295 | 
             
            				updateStatus('Connected to ReachyMini', 'connected');
         | 
| 296 | 
             
            				addLog('Successfully connected!', 'success');
         | 
|  | |
| 307 | 
             
            			}
         | 
| 308 | 
             
            		}
         | 
| 309 |  | 
| 310 | 
            +
            		function handleResponse(event) {
         | 
| 311 | 
            +
            			const value = event.target.value;
         | 
| 312 | 
            +
            			const decoder = new TextDecoder('utf-8');
         | 
| 313 | 
            +
            			const response = decoder.decode(value);
         | 
| 314 | 
            +
            			addLog(`Response: ${response}`, 'success');
         | 
| 315 | 
            +
            			showResponse(response);
         | 
| 316 | 
            +
            		}
         | 
| 317 | 
            +
             | 
| 318 | 
             
            		function onDisconnected() {
         | 
| 319 | 
             
            			updateStatus('Disconnected', 'disconnected');
         | 
| 320 | 
             
            			addLog('Device disconnected', 'error');
         | 
| 321 | 
             
            			connectBtn.textContent = 'Connect to ReachyMini';
         | 
| 322 | 
             
            			commandBtns.forEach(btn => btn.disabled = true);
         | 
| 323 | 
            +
            			commandCharacteristic = null;
         | 
| 324 | 
            +
            			responseCharacteristic = null;
         | 
| 325 | 
             
            			device = null;
         | 
| 326 | 
             
            		}
         | 
| 327 |  | 
|  | |
| 333 | 
             
            		}
         | 
| 334 |  | 
| 335 | 
             
            		async function sendCommand(command) {
         | 
| 336 | 
            +
            			if (!commandCharacteristic) {
         | 
| 337 | 
             
            				addLog('Not connected to device', 'error');
         | 
| 338 | 
             
            				return;
         | 
| 339 | 
             
            			}
         | 
|  | |
| 341 | 
             
            			try {
         | 
| 342 | 
             
            				const encoder = new TextEncoder();
         | 
| 343 | 
             
            				const data = encoder.encode(command);
         | 
| 344 | 
            +
            				await commandCharacteristic.writeValue(data);
         | 
| 345 | 
             
            				addLog(`Sent command: ${command}`, 'success');
         | 
| 346 | 
            +
             | 
| 347 | 
            +
            				// Try to read the response
         | 
| 348 | 
            +
            				try {
         | 
| 349 | 
            +
            					const value = await responseCharacteristic.readValue();
         | 
| 350 | 
            +
            					const decoder = new TextDecoder('utf-8');
         | 
| 351 | 
            +
            					const response = decoder.decode(value);
         | 
| 352 | 
            +
            					if (response) {
         | 
| 353 | 
            +
            						addLog(`Response: ${response}`, 'success');
         | 
| 354 | 
            +
            						showResponse(response);
         | 
| 355 | 
            +
            					}
         | 
| 356 | 
            +
            				} catch (readError) {
         | 
| 357 | 
            +
            					// Response will come via notification
         | 
| 358 | 
            +
            					addLog('Waiting for response notification...', '');
         | 
| 359 | 
            +
            				}
         | 
| 360 | 
             
            			} catch (error) {
         | 
| 361 | 
             
            				addLog(`Failed to send command: ${error.message}`, 'error');
         | 
| 362 | 
             
            				console.error('Send error:', error);
         | 

