- Python 98.2%
- Dockerfile 1.8%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| biotime.py | ||
| CLAUDE.md | ||
| compose.yaml | ||
| crontab | ||
| Dockerfile | ||
| monitor.py | ||
| README.md | ||
| weekly_report.py | ||
biotime-scripts
Scripts para sacarle provecho a un servidor ZKTeco BioTime (8.x/9.x) usando su API REST:
monitor.py: avisa por correo cuando BioTime deja de responder o cuando un checador pierde conexión, y otra vez cuando se recupera.weekly_report.py: manda por correo el reporte de asistencias del periodo de nómina (sábado a viernes). Incluye horas por empleado, checadas incompletas y quién no checó nada.
Solo usan la librería estándar de Python (3.12 o más reciente), sin dependencias que instalar.
Configuración
cp .env.example .env
chmod 600 .env
| Variable | Descripción |
|---|---|
BIOTIME_URL |
URL pública de BioTime, por ejemplo https://biotime.ejemplo.com |
USER / PASSWORD |
Usuario de BioTime. Tiene que ser superusuario: la API no da tokens a otros usuarios |
SMTP_HOST, SMTP_PORT, SMTP_SECURITY |
Servidor de correo saliente. SMTP_SECURITY puede ser starttls, ssl o none |
SMTP_USER / SMTP_PASSWORD |
Cuenta de correo. En Gmail se usa una contraseña de aplicación |
SMTP_FROM |
Nombre visible (BioTime) o dirección completa (BioTime <correo@dominio.com>) |
MAIL_TO |
Destinatarios del reporte, separados por coma |
ALERT_MAIL_TO |
Destinatarios de las alertas del monitor. Si está vacío se usa MAIL_TO |
REPORT_TITLE |
Título que aparece dentro del correo del reporte |
DEVICE_MAX_MINUTES |
Minutos que puede pasar un checador sin conectarse antes de mandar alerta (10 por defecto) |
EXCLUDE_EMP_CODES |
IDs de empleados que no deben salir en el reporte (usuarios de prueba, administradores) |
Uso
# Monitor: no manda correo si nada cambió desde la última revisión
python3 monitor.py
python3 monitor.py --dry-run # imprime el resultado y no guarda estado
# Reporte del último periodo sábado–viernes completo
python3 weekly_report.py
python3 weekly_report.py --dry-run # no manda correo: guarda reporte.html
python3 weekly_report.py --desde 2026-09-05 --dry-run # periodo de 7 días a partir de esa fecha
Docker (Unraid)
compose.yaml levanta un contenedor con crond que corre el monitor cada 5 minutos y el reporte el sábado a las 9:00 de México. A esa hora ya marcaron salida los turnos nocturnos del viernes. El horario está en crontab y usa la hora de México (TZ=America/Mexico_City).
El .env y .monitor_state.json se guardan en el volumen /data. Por defecto es /mnt/user/appdata/biotime, y se cambia con la variable BIOTIME_DATA_DIR al correr docker compose. En CI se toma de la variable de Actions BIOTIME_DATA_DIR del repo (Configuración → Acciones → Variables). Antes del primer deploy:
mkdir -p /mnt/user/appdata/biotime # o la ruta de BIOTIME_DATA_DIR
cp .env.example /mnt/user/appdata/biotime/.env # y llénalo
chmod 600 /mnt/user/appdata/biotime/.env
docker logs -f biotime # salida del monitor y del reporte
docker exec biotime python monitor.py --dry-run
docker exec biotime python weekly_report.py --dry-run # deja reporte.html en /data
Fuera de Docker, los scripts buscan .env en su propia carpeta. DATA_DIR cambia esa ubicación.
Pruebas
Usan unittest y datos ficticios; no se conectan a BioTime ni mandan correos.
python3 -m unittest # local
docker build --target test . # igual que en CI, con Python 3.12
CI/CD (Forgejo Actions)
.forgejo/workflows/ci.yml:
- Pull request a
main: corre las pruebas (test). - Push a
main(incluye los merges): corre las pruebas y, si pasan, hace deploy condocker compose -p biotime up -d --build.
Requisitos:
- Runner en el mismo Unraid, con la etiqueta
dockery que monte el socket de Docker en los jobs (por ejemplocontainer.docker_host: automounten su configuración). No se agrega-v /var/run/docker.socken el workflow: si el runner ya lo monta, Docker falla con Duplicate mount point. - Protección de
main(Configuración → Ramas → Agregar regla): desactiva el push directo y activa Requerir que pasen las verificaciones de estado con el patrónCI / test*. Así no se puede hacer merge si las pruebas fallan.
Programación con cron (sin Docker)
Horas en UTC (México centro = UTC-6):
*/5 * * * * cd ~/biotime-scripts && python3 monitor.py >> monitor.log 2>&1
0 15 * * 6 cd ~/biotime-scripts && python3 weekly_report.py >> report.log 2>&1
Cómo se arma el reporte
- Cada entrada se empareja con la siguiente salida, siempre que no pasen más de 16 horas entre las dos.
- Un turno cuenta en el día de su entrada. Así, el turno nocturno del viernes entra en el periodo aunque la salida sea el sábado en la mañana.
- Si hay checadas del mismo empleado con menos de 2 minutos de diferencia, se cuenta solo la primera.
07:00–?significa entrada sin salida,?–07:00salida sin entrada y(+1)que la salida fue al día siguiente.
Problemas conocidos de BioTime
- Checador en el área "Default (Reservado)": BioTime no intercambia datos con ese checador. Hay que pasarlo a un área normal, que debe ser la misma de los empleados.
- Solo se envían al checador los empleados de su misma área.
- La paginación de la API regresa enlaces
nextcon la dirección interna (http://127.0.0.1:<puerto>/...).biotime.pysolo toma la ruta y la consulta de ese enlace. - Zona horaria
Etc/GMT-6: con la nomenclatura estándar, el signo va al revés y significa UTC+6. Revisa que la hora de las checadas salga correcta.