---
name: oo_bridge_send
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def oo_bridge_send(cmd: str, ref: str = '', text: str = '', opts: dict = None, target: str = 'cell', port: int = 8791, wait_s: float = 6.0) -> dict"
description: "Cliente programatico del puente OnlyOffice (app onlyoffice_bridge): encola un comando de la Automation API contra el documento OnlyOffice Desktop abierto y en foco, hace polling del resultado y lo devuelve como dict no-throw ({status: ok, result} o {status: error, error}). Lee y edita en vivo hojas (Cell), documentos (Word) y presentaciones sin cerrar/reabrir. Solo stdlib."
tags: [onlyoffice, bridge, live-edit, http, automation, spreadsheet, infra]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [json, time, urllib.request, urllib.error]
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/infra/oo_bridge_send.py"
params:
  - name: cmd
    desc: "Nombre del comando de la Automation API. Cell lectura: get_cell, get_range, get_formula, get_used_range, get_sheets. Cell escritura: set_cell, set_range, set_formula. Estructura: insert_rows, delete_rows, insert_cols, delete_cols, merge, add_sheet, rename_sheet, delete_sheet, activate_sheet. Formato: set_format, set_borders, set_colwidth, set_rowheight. Datos: add_named_range, sort, autofilter. Graficos/tablas: add_chart, format_as_table, table_chart. Word: insert_text, get_text."
  - name: ref
    desc: "Referencia A1 o rango sobre la que actua el comando: celda ('B2'), rango ('A1:C10'), filas ('4:6' para insert_rows/delete_rows), columnas ('B:C' para insert_cols/delete_cols). Vacio para comandos sin ref (get_sheets, add_sheet, get_used_range, insert_text)."
  - name: text
    desc: "Valor textual para comandos de escritura: valor de celda (set_cell), formula '=SUM(A1:A9)' (set_formula), nombre de hoja (add_sheet/rename_sheet/activate_sheet), texto a insertar (insert_text). Vacio para lecturas."
  - name: opts
    desc: "Dict con parametros extra segun el comando. values (matriz 2D para set_range), sheet (hoja destino por nombre en cualquier comando cell, sin cambiar la activa), bold/italic/underline/fill=[r,g,b]/fontColor=[r,g,b]/numFormat/fontName/fontSize/wrap/alignH/alignV (set_format), style (set_borders), width (set_colwidth), height (set_rowheight), by/desc (sort), type/dataRange/pos (add_chart), dataRange/type (table_chart). None se trata como {}."
  - name: target
    desc: "Editor destino: 'cell' (Spreadsheet, por defecto), 'word' (Document), 'slide' (Presentation). El server enruta el comando al editor con foco que coincida."
  - name: port
    desc: "Puerto del server puente loopback (server.py). Default 8791."
  - name: wait_s
    desc: "Segundos de polling a /pull antes de devolver un error de timeout. Default 6.0. Subir si el editor tarda en responder comandos pesados (add_chart, set_range grande)."
output: "Dict no-throw. Exito: {'status': 'ok', 'result': <valor>} donde <valor> es lo que devuelve el plugin (string de celda, matriz 2D para get_range, lista de nombres de hoja para get_sheets, o 'ok' para escrituras). Error: {'status': 'error', 'error': <msg>} si el plugin devolvio {error:...}, si vencio el timeout (editor sin foco o plugin no arrancado), o si hubo fallo de red (server no corriendo). NUNCA lanza excepcion."
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from infra import oo_bridge_send

# Leer una celda de la hoja con foco
r = oo_bridge_send("get_cell", ref="B2")
print(r)                       # {'status': 'ok', 'result': '42'}

# Escribir una celda
oo_bridge_send("set_cell", ref="B2", text="123")

# Escritura batch 2D en un rango
oo_bridge_send("set_range", ref="D1:E2",
               opts={"values": [["a", 1], ["b", 2]]})

# Formula
oo_bridge_send("set_formula", ref="F1", text="=SUM(A1:A9)")

# Leer un rango entero (matriz 2D)
r = oo_bridge_send("get_range", ref="A1:C10")
for row in r["result"]:
    print(row)

# Listar hojas y crear una nueva
print(oo_bridge_send("get_sheets")["result"])   # ['Hoja1', 'Datos']
oo_bridge_send("add_sheet", text="Resumen")

# Formato: negrita + relleno + formato numerico, sobre otra hoja por nombre
oo_bridge_send("set_format", ref="A1:C1",
               opts={"bold": True, "fill": [230, 230, 250],
                     "numFormat": "0.00%", "sheet": "Datos"})

# Insertar 3 filas en la fila 4 / borrar columnas B y C
oo_bridge_send("insert_rows", ref="4:6")
oo_bridge_send("delete_cols", ref="B:C")

# Ordenar un rango por una columna, descendente
oo_bridge_send("sort", ref="A1:D20", opts={"by": "B1", "desc": True})

# Grafico de barras a partir de un rango
oo_bridge_send("add_chart",
               opts={"type": "bar", "dataRange": "A1:B10", "pos": [5, 1]})

# Word: insertar y leer texto
oo_bridge_send("insert_text", text="Nuevo parrafo", target="word")
print(oo_bridge_send("get_text", target="word")["result"])
```

## Cuando usarla

Cuando necesites leer o editar EN VIVO un documento OnlyOffice Desktop ya abierto
(hoja de calculo, documento o presentacion) sin cerrarlo ni reabrirlo — por ejemplo
para rellenar celdas, aplicar formato, crear graficos o volcar una matriz calculada
directamente sobre la ventana que el usuario tiene delante. Es el bloque de
construccion de bajo nivel: un comando por llamada. Componla en pipelines o heredocs
para secuencias (crear hoja -> volcar datos -> formatear cabecera -> anadir grafico).
Prefierela sobre editar el `.xlsx`/`.docx` en disco (`write_xlsx_sheets_py_infra`,
etc.) cuando el archivo esta abierto en OnlyOffice, porque el editor Desktop NO
recarga cambios externos del disco (GitHub issue #2313).

## Gotchas

- **El editor debe estar ABIERTO y con la ventana en FOCO** para que el plugin de
  sistema arranque y procese comandos. Si no hay foco en el editor destino, el
  polling vence y devuelve `{"status": "error", "error": "timeout: ..."}`. Abrir con
  `open_onlyoffice_file_bash_shell` y traer la ventana al frente antes de enviar.
- **El server puente (`apps/onlyoffice_bridge/server.py`) debe estar corriendo** en
  el puerto indicado (`python3 apps/onlyoffice_bridge/server.py 8791 &`, idealmente
  como service systemd --user). Si no corre, la funcion devuelve
  `{"status": "error", "error": "push failed: ... (is server.py running ...)"}`.
- **Persistencia a disco = Ctrl+S del usuario.** Estas ediciones ocurren sobre el
  documento vivo en memoria; el guardado automatizado es intermitente. Para dejar el
  cambio en el archivo, el usuario guarda con Ctrl+S (o usar
  `save_onlyoffice_file_bash_shell`, que no siempre confirma).
- **Tablas nativas (ListObjects): solo se pueden CREAR** con `format_as_table`. Este
  build de la Automation API NO soporta listar ni re-estilizar tablas nativas
  existentes — `get_tables`/`set_table_style` no devuelven datos utiles. Para
  visualizar datos tabulados usa rangos + `set_format` o crea la tabla de cero.
- La funcion NUNCA lanza: SIEMPRE inspecciona `result["status"]` antes de usar
  `result["result"]`. Un `{"status": "ok", "result": None}` significa que el plugin
  respondio vacio (comando aplicado sin valor de retorno).
- `opts.sheet` apunta a una hoja por nombre en cualquier comando cell sin cambiar la
  hoja activa; util para editar en segundo plano.
- Comandos pesados (`set_range` de miles de celdas, `add_chart`) pueden tardar mas de
  6s; subir `wait_s` para esos casos.
