Send JSON-RPC command and wait for response with mandatory ID correlation (async). Args: function: RPC function name args: Function arguments as list (default: []) timeout: Override default timeout for this call retries: Number of retry attemp
(
self,
function: str,
args: list[Any] | None = None,
timeout: float | None = None,
retries: int = 1,
)
| 332 | return lines_drained |
| 333 | |
| 334 | async def send( |
| 335 | self, |
| 336 | function: str, |
| 337 | args: list[Any] | None = None, |
| 338 | timeout: float | None = None, |
| 339 | retries: int = 1, |
| 340 | ) -> RpcResponse: |
| 341 | """Send JSON-RPC command and wait for response with mandatory ID correlation (async). |
| 342 | |
| 343 | Args: |
| 344 | function: RPC function name |
| 345 | args: Function arguments as list (default: []) |
| 346 | timeout: Override default timeout for this call |
| 347 | retries: Number of retry attempts (default: 1 = no retries) |
| 348 | |
| 349 | Returns: |
| 350 | RpcResponse with parsed data and matching request ID (ID always present) |
| 351 | |
| 352 | Raises: |
| 353 | RpcTimeoutError: If no response within timeout |
| 354 | RpcError: If not connected |
| 355 | """ |
| 356 | if not self.is_connected: |
| 357 | raise RpcError("Not connected") |
| 358 | |
| 359 | assert self._serial is not None |
| 360 | |
| 361 | # Generate unique request ID for JSON-RPC 2.0 correlation (mandatory) |
| 362 | request_id = self._next_id |
| 363 | self._next_id = (self._next_id + 1) & 0xFFFFFFFF # Wrap at uint32 max |
| 364 | |
| 365 | # Wrap args in array to pass as single Json parameter to RPC functions |
| 366 | # RPC functions expect signature: (const fl::json& args) |
| 367 | # So we need to pass the entire args as one parameter |
| 368 | wrapped_args: list[Any] = [args] if args is not None else [{}] |
| 369 | cmd: dict[str, Any] = { |
| 370 | "method": function, |
| 371 | "params": wrapped_args, |
| 372 | "id": request_id, # ID is mandatory |
| 373 | } |
| 374 | cmd_str = json.dumps(cmd, separators=(",", ":")) |
| 375 | |
| 376 | if self.verbose: |
| 377 | print(f"📤 [RPC] Sending: {cmd_str}") |
| 378 | |
| 379 | effective_timeout = timeout if timeout is not None else self.timeout |
| 380 | attempt_timeout = effective_timeout / max(retries, 1) |
| 381 | |
| 382 | last_error: Exception | None = None |
| 383 | |
| 384 | for _attempt in range(retries): |
| 385 | try: |
| 386 | # Write command via serial interface |
| 387 | await self._serial.write(cmd_str + "\n") |
| 388 | |
| 389 | # Await response with ID matching (mandatory) |
| 390 | response = await self._wait_for_response( |
| 391 | attempt_timeout, expected_id=request_id |