Stempelbot 🤖⏱️

A small Streamlit-based helper that automates and visualizes stamping in/out on the coeo employee portal (mitarbeiterinfo.coeo.de). It provides a live dashboard of your current working day, lets you stamp manually with one click, and can auto-stamp at a scheduled time in the background.

Features

  • Live work-day metrics – start time, target end time (8h), net/gross work, taken breaks and legally required break deductions, updated every few seconds.
  • Manual stamping – single button that logs in to the coeo portal and sends a stamp request, storing the timestamp locally.
  • SmartTime Plus verification – after every stamp the app opens the internal SmartTime Plus dashboard (headless Chromium) and confirms the "Letzte Buchung" actually moved. Failed transmissions are surfaced instead of silently missed.
  • Auto-Stamp planner – schedule one or more times for today; a background thread fires the stamp within a 2-minute window of the target time and cleans up missed entries.
  • Daily timeline – two-column "Kommen / Gehen" view of every stamp of the day.
  • Local persistence – stamps and scheduled auto-stamps are stored as JSON files in the project directory (timestamp_history.json, autostamps.json).

Project layout

stempelbot/
    app.py             # Streamlit UI
    cli.py             # `stempelbot` entry point → runs `streamlit run app.py`
    client.py          # CoeoClient: login + stamp HTTP calls
    autostamp.py       # AutoStampService: background scheduler
    stamp_history.py   # Local JSON stamp history
    time_calculator.py # Work/break time calculations
    settings.py        # Pydantic settings loaded from .env

Installation

Requires Python 3.12+ and Poetry.

poetry install
poetry run playwright install chromium

The playwright install step downloads a ~150 MB Chromium build that the SmartTime verifier uses in headless mode. It only needs to be run once.

.env setup

settings.py uses pydantic-settings and expects a .env file in the working directory from which you start the app (typically the project root). All variables are required:

# Credentials for https://mitarbeiterinfo.coeo.de
USERNAME=your.user
PASSWORD=your-password

# Password for the internal SmartTime Plus web app (username is USERNAME).
# Used to verify that stamps actually reached the backend.
SMART_TIME_PASSWORD=your-smarttime-password

# Optional overrides:
# SMART_TIME_URL=https://zeiterfassung/
# SMART_TIME_VERIFY_INITIAL_DELAY=1   # seconds to wait before the first check
# SMART_TIME_VERIFY_GRACE_PERIOD=30   # total polling budget in seconds
# SMART_TIME_VERIFY_POLL_INTERVAL=3   # seconds between polls
# SMART_TIME_VERIFY_TOLERANCE_SEC=90  # allowed drift between stamp & backing time

# Paths (relative or absolute) to the JSON files used for local persistence.
# The files will be created on first write.
AUTOSTAMP_FILE=autostamps.json
STAMPHISTORY_FILE=timestamp_history.json

# Python logging level, e.g. DEBUG / INFO / WARNING / ERROR
LOG_LEVEL=INFO

⚠️ Credentials are sent to the coeo portal from your local machine. Keep the .env file out of version control.

Usage

Start the Streamlit app via the Poetry script:

poetry run stempelbot

This is equivalent to streamlit run stempelbot/app.py. Any extra CLI arguments are forwarded to Streamlit, e.g.:

poetry run stempelbot --server.port 8502

Then open the URL Streamlit prints (default: http://localhost:8501).

Manual stamp

Click "Jetzt Stempeln" – the client logs in and posts the stamp request; on success the current time is appended to timestamp_history.json.

Auto-Stamp

  1. Pick a time in the Auto-Stempel Planer section.
  2. Click "Hinzufügen" – the entry is written to autostamps.json.
  3. A background thread (AutoStampService) checks every 30 seconds and fires the stamp when the current time is within a 2-minute window of the target. Missed entries (older than 2 minutes) are cleaned up automatically.

Because the scheduler runs inside the Streamlit process, the app must be running for auto-stamps to fire.

Daily view

The "Heutiger Verlauf" section lists all of today's stamps split into Kommen (even index) and Gehen (odd index) columns.

How stamping works

CoeoClient in client.py performs two HTTP POSTs against https://mitarbeiterinfo.coeo.de:

  1. POST /ppeiiefdna_login with httpd_username / httpd_password.
  2. POST /index.php with c=zeiterfassung&homeoffice=1 to toggle the stamp.

The session cookie from step 1 is reused for step 2. HTTP 200 is treated as success.

Because a 200 from the coeo portal does not guarantee the stamp reached the downstream time-tracking system, SmartTimeClient (see smarttime_client.py) then uses a headless Chromium (Playwright) to log in to SmartTime Plus (https://zeiterfassung/), navigate to the dashboard, and read the "Letzte Buchung" value. If it doesn't match the sent stamp within SMART_TIME_VERIFY_TOLERANCE_MIN after SMART_TIME_VERIFY_DELAY seconds, the UI flags the stamp as unverified.

SmartTime Plus is a GWT app (all traffic is text/x-gwt-rpc with per-build strong-name hashes), so we drive the DOM instead of trying to speak the RPC protocol. A persistent Chromium profile in ~/.stempelbot/smarttime-profile keeps session cookies between runs so subsequent verifications skip the login step.

SmartTime Abgleich panel

Below the daily timeline there's a "SmartTime Abgleich" panel with a "Jetzt prüfen" button that compares the last local stamp with the value reported by SmartTime Plus and highlights any mismatch.

Notes

  • Time calculations (net work, break deductions, target end time) live in time_calculator.py.
  • Streamlit caches the CoeoClient (lru_cache) and the AutoStampService (st.cache_resource) so login state and the scheduler thread survive reruns.
  • This project targets the internal coeo portal and is not affiliated with coeo.
S
Description
No description provided
Readme
351 KiB
Languages
Python 100%