---
name: dav_list_calendars
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def dav_list_calendars(base_url: str, username: str, password: str, home_path: str, *, timeout_s: float = 15.0, verify_tls: bool = True) -> dict"
description: "Lista las colecciones de calendario CalDAV bajo un calendar-home en UNA peticion PROPFIND Depth:1. Devuelve solo las colecciones hijas que son calendarios CalDAV de verdad (resourcetype {urn:ietf:params:xml:ns:caldav}calendar), cada una con su href, su displayname (DAV) y su color (calendar-color de Apple, ej. #FF2968FF) si el servidor lo expone. El propio calendar-home (coleccion plana sin el resourcetype calendar) y cualquier coleccion no-calendario se excluyen. Considera solo los propstat con estado 2xx para no leer props marcadas 404. Construye Authorization: Basic base64(user:pass) a mano y parsea el multistatus con regex. verify_tls=True por defecto. Maneja errores sin lanzar. Solo stdlib (urllib, base64, re, ssl, html). Probado contra Xandikos."
tags: [dav, caldav, calendar, calendars, propfind, displayname, color, selector, http, infra]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [base64, html, re, ssl, urllib.error, urllib.request]
params:
  - name: base_url
    desc: "URL base del servidor DAV (p.ej. 'https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com')."
  - name: username
    desc: "usuario para HTTP Basic auth (p.ej. 'enmanuel')."
  - name: password
    desc: "contrasena para HTTP Basic auth. Resolver desde pass con pass_get_secret, nunca hardcodear."
  - name: home_path
    desc: "ruta del calendar-home del usuario (p.ej. '/enmanuel/calendars/'). Las colecciones de calendario cuelgan de el."
  - name: timeout_s
    desc: "timeout de la peticion HTTP en segundos. Default 15.0."
  - name: verify_tls
    desc: "si True (default) verifica el certificado TLS. No desactivar salvo entorno de prueba."
output: "dict. En exito: {status:'ok', http_status:int, calendars:[{href:str, name:str, color:str|None}, ...]} con un elemento por coleccion de calendario, ordenadas por nombre. color es el calendar-color de Apple (ej. '#FF2968FF') o None. En error (sin lanzar): {status:'error', error:str, http_status:int|None}."
tested: true
tests:
  - "test_lista_solo_calendarios"
  - "test_excluye_home_plano"
  - "test_extrae_nombre_y_color"
  - "test_color_ausente_es_none"
  - "test_ignora_props_404"
  - "test_httperror_devuelve_status_error"
  - "test_urlerror_sin_red"
test_file_path: "python/functions/infra/dav_list_calendars_test.py"
file_path: "python/functions/infra/dav_list_calendars.py"
---

## Ejemplo

```python
import sys
sys.path.insert(0, "python/functions")
from infra.pass_get_secret import pass_get_secret
from infra.dav_list_calendars import dav_list_calendars

pw = pass_get_secret("dav/xandikos-enmanuel")["value"]  # NO logear

res = dav_list_calendars(
    base_url="https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com",
    username="enmanuel",
    password=pw,
    home_path="/enmanuel/calendars/",
)
for c in res["calendars"]:
    print(c["name"], c["href"], c["color"])
# calendar /enmanuel/calendars/calendar/ None
```

## Cuando usarla

Cuando una UI necesita un selector de calendario: el usuario tiene varias
colecciones CalDAV bajo su calendar-home y quiere elegir una, con su nombre y su
color para pintarla. Devuelve el href de cada calendario (lo que luego pasas a
`dav_get_collection` / `caldav_put_event` como `collection_path`) sin que el
caller tenga que conocerlos de antemano ni distinguir el calendar-home de los
calendarios reales.

## Gotchas

- Filtra por el resourcetype `caldav:calendar`: el propio calendar-home es una
  coleccion plana (sin ese resourcetype) y queda fuera, igual que carpetas
  intermedias. Si tu servidor anida calendarios mas profundo que Depth:1, llama
  con `home_path` apuntando al nivel correcto.
- El `color` es el `calendar-color` de Apple (`http://apple.com/ns/ical/`), que
  Xandikos puede no tener seteado (devuelve None). Apple usa formato `#RRGGBBAA`
  (8 digitos, con alfa); recortalo a `#RRGGBB` si tu UI no soporta alfa.
- Solo lee los `<propstat>` con estado 2xx para no confundir un
  `<calendar-color/>` vacio de un propstat 404 con un color real.
- Lectura remota real sobre TLS; password de `pass`, no se logea.
