diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index 433c4a3..d11ecbd 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -3,8 +3,14 @@ name: Build & Deploy moonweb-site on: push: branches: [main] + paths-ignore: + - 'stefankoelle/**' + - '.github/workflows/deploy-stefankoelle.yml' pull_request: branches: [main] + paths-ignore: + - 'stefankoelle/**' + - '.github/workflows/deploy-stefankoelle.yml' # Cancel in-progress runs for the same branch concurrency: diff --git a/.github/workflows/deploy-stefankoelle.yml b/.github/workflows/deploy-stefankoelle.yml new file mode 100644 index 0000000..a966b8c --- /dev/null +++ b/.github/workflows/deploy-stefankoelle.yml @@ -0,0 +1,107 @@ +name: Build & Deploy stefankoelle.de + +on: + push: + branches: [main] + paths: + - 'stefankoelle/**' + - 'shared/**' + - '.github/workflows/deploy-stefankoelle.yml' + pull_request: + branches: [main] + paths: + - 'stefankoelle/**' + - 'shared/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + NODE_VERSION: "22" + +jobs: + build: + name: Build stefankoelle.de + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Build stefankoelle + run: npm run build:stefankoelle + + - name: Validate build output + run: | + if [ ! -d "dist/stefankoelle" ]; then + echo "Error: dist/stefankoelle not found" + exit 1 + fi + if [ -z "$(ls -A dist/stefankoelle 2>/dev/null)" ]; then + echo "Error: dist/stefankoelle is empty" + exit 1 + fi + echo "Build output:" + find dist/stefankoelle -type f | head -20 + + - name: Upload build artifact + uses: actions/upload-artifact@v7 + with: + name: dist-stefankoelle + path: dist/stefankoelle + retention-days: 7 + + deploy: + name: Deploy stefankoelle.de + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + steps: + - name: Download build artifact + uses: actions/download-artifact@v8 + with: + name: dist-stefankoelle + path: dist/stefankoelle + + - name: Deploy via SFTP to IONOS + uses: wlixcc/sftp-deploy-action@v1 + with: + local_path: './dist/stefankoelle/*' + server: ${{ secrets.IONOS_SFTP_HOST }} + username: ${{ secrets.IONOS_SFTP_USER }} + password: ${{ secrets.IONOS_SFTP_PASSWORD }} + server_path: '/deploy/stefankoelle/' + port: 22 + + summary: + name: Deploy Summary + needs: deploy + runs-on: ubuntu-latest + if: always() + steps: + - name: Print deployment summary + run: | + echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + if [ "${{ needs.deploy.result }}" = "success" ]; then + echo "✅ stefankoelle.de deployed successfully!" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Deployment failed. Check the deploy job for details." >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Site" >> $GITHUB_STEP_SUMMARY + echo "| Site | URL |" >> $GITHUB_STEP_SUMMARY + echo "|------|-----|" >> $GITHUB_STEP_SUMMARY + echo "| stefankoelle | https://stefankoelle.de |" >> $GITHUB_STEP_SUMMARY + +# Required repo secrets: +# IONOS_SFTP_HOST — IONOS SFTP server hostname +# IONOS_SFTP_USER — IONOS SFTP username +# IONOS_SFTP_PASSWORD — IONOS SFTP password diff --git a/AGENTS.md b/AGENTS.md index 370d47c..8878f56 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,9 +2,9 @@ ## Projektübersicht -Monorepo für 5 statische Websites unter moonweb.org, basierend auf Eleventy (11ty), deployt auf Cloudflare Pages. +Monorepo für 5 statische Websites unter moonweb.org + stefankoelle.de, basierend auf Eleventy (11ty). -### Websites +### Websites (Cloudflare Pages) | Site | Domain | Zweck | Status | |------|--------|-------|--------| @@ -14,82 +14,76 @@ Monorepo für 5 statische Websites unter moonweb.org, basierend auf Eleventy (11 | code | code.moonweb.org | GitHub-Projekte (aggregiert via .moonweb.yml) | Fertig (10 Repos) | | retro | retro.moonweb.org | Physische Retro-Hardware | Nur Übersicht (WIP) | -### Externe Sites (nicht im Monorepo) +### Externe Sites (SFTP-Deployment) + +| Site | Domain | Zweck | Status | +|------|--------|-------|--------| +| stefankoelle | stefankoelle.de | Lebenslauf, Kontakt, Projekte | Onepager + LED-Matrix-Detailseite | + +### Andere Sites (nicht im Monorepo) -- stefankoelle.de — Lebenslauf, Kontakt (extern gehostet) - www.moonweb.org — 2000er Internet-Zeitkapsel - 28k8.moonweb.org — 90er BBS/Scene-Archiv - ## Dateistruktur ``` moonweb-site/ ├── hub/ # Eleventy-Config + index.njk ├── infra/ # Eleventy-Config + index.njk + 3 Subseiten -│ ├── backup-strategy/ -│ ├── monitoring/ -│ └── dev-workflow/ ├── smarthome/ # Eleventy-Config + index.njk + 6 Subseiten -│ ├── homematic-mqtt/ -│ ├── tasmota-energy/ -│ ├── balkonpi/ -│ ├── airplay-audio/ -│ ├── octoprint/ -│ └── tubearchivist/ ├── code/ # Eleventy-Config + index.njk -│ └── _data/repos.json # Vom GitHub-Aggregator generiert +│ └── _data/repos.json ├── retro/ # Eleventy-Config + index.njk +├── stefankoelle/ # Eleventy-Config + Onepager +│ ├── eleventy.config.js +│ ├── _includes/ +│ │ ├── stefankoelle.njk # Layout (Header, Nav, Footer) +│ │ └── cv-content.njk # CV-Partial (zentral fuer Web + PDF) +│ ├── index.njk # Onepager (alle Sections) +│ ├── cv-print.njk # CV-only fuer spaeteren PDF-Druck +│ ├── assets/ # CSS, JS, Bilder, Favicons +│ ├── ledmatrix/ # LED Matrix WebServer Dokumentation +│ └── pdf/ # PDF-CSS (fuer spaeteren WeasyPrint-Bau) ├── shared/ # Gemeinsame Komponenten │ ├── _includes/ │ │ ├── base.njk # Basis-Layout (Header, Site-Switcher, Footer) │ │ └── card-grid.njk # Card-Grid Template -│ ├── base.css # Basis-CSS (Layout, Cards, Typography) -│ └── theme-*.css # Accent-Farben pro Site +│ ├── base.css +│ └── theme-*.css ├── scripts/ -│ └── github-aggregator/ # Python-Skript für code.moonweb.org -│ ├── aggregate.py -│ ├── example.moonweb.yml -│ └── README.md +│ └── github-aggregator/ ├── .github/workflows/ -│ └── build-deploy.yml # CI/CD: Build + Deploy zu Cloudflare Pages -├── DESIGN.md # Initiales Konzept mit Design-Entscheidungen (deutsch) -├── SPEC.md # Vollständige Spezifikation (148 Zeilen) -├── PLAN.md # Implementierungsplan (65 Zeilen) -├── README.md # Projekt-README -├── TODO.md # Offene Items + Workflow -└── package.json # npm scripts für dev/build +│ ├── build-deploy.yml # CI/CD: Cloudflare Pages (5 Sites) +│ └── deploy-stefankoelle.yml # CI/CD: IONOS SFTP (stefankoelle.de) +├── DESIGN.md +├── SPEC.md +├── PLAN.md +├── README.md +├── TODO.md +└── package.json ``` -## Dokumentation - -- `DESIGN.md` — Initiales Konzept mit Design-Entscheidungen (deutsch) -- `SPEC.md` — Vollständige Spezifikation (148 Zeilen) -- `PLAN.md` — Implementierungsplan (65 Zeilen) -- `README.md` — Projekt-README -- `TODO.md` — Offene Items + Workflow für weitere Arbeit -- `TODO.md` — Offene Items + Workflow für weitere Arbeit - ## Technischer Stack - **SSG:** Eleventy (11ty) v3.1.6 - **Templates:** Nunjucks (.njk) - **CSS:** Variables-basiert mit Accent-Farben pro Site -- **Deploy:** Cloudflare Pages (5 separate Projects) -- **CI/CD:** GitHub Actions -- **GitHub-Aggregator:** Python (liest .moonweb.yml aus Repos) -- **Deploy:** Cloudflare Pages (5 separate Projects) +- **Deploy (moonweb):** Cloudflare Pages (5 separate Projects) +- **Deploy (stefankoelle):** IONOS SFTP +- **CI/CD:** GitHub Actions (2 Workflows) ## Entwicklung ```bash npm install -npm run dev:hub # localhost:8081 -npm run dev:infra # localhost:8082 -npm run dev:smarthome # localhost:8083 -npm run dev:code # localhost:8084 -npm run dev:retro # localhost:8085 -npm run build # Alle Sites bauen +npm run dev:hub # localhost:8081 +npm run dev:infra # localhost:8082 +npm run dev:smarthome # localhost:8083 +npm run dev:code # localhost:8084 +npm run dev:retro # localhost:8085 +npm run dev:stefankoelle # localhost:8086 +npm run build # Alle Sites bauen ``` ## Design-Prinzipien @@ -101,36 +95,35 @@ npm run build # Alle Sites bauen 5. **Keine Analytics** — Keine Tracking-Tools 6. **Sensible Daten** — Infra-Content wird manuell redigiert (keine IPs, Keys, Passwörter) -## Content-Regeln (SPEC.md §5) +## stefankoelle.de -| Site | Detailseiten? | Regel | -|------|---------------|-------| -| smarthome | Ja | Nur wenn genug Content vorhanden | -| code | Nie | Nur Übersicht + GitHub-Links | -| infra | Kaum | Bewusst oberflächlich (sensible Daten) | -| retro | Ja, minimal | Rudimentär, WIP erlaubt | - -## GitHub-Aggregator - -`scripts/github-aggregator/aggregate.py` liest `.moonweb.yml` aus allen public Repos von `skoelle` und schreibt `code/_data/repos.json`. Manuell ausführen: - -```bash -export GITHUB_TOKEN=ghp_xxx -python scripts/github-aggregator/aggregate.py -``` +- Eigene Eleventy-Config (nicht shared base.njk) +- Eigenes CSS-Design (nicht moonweb design system) +- Onepager mit Anchor-Links (bleibt so) +- Deploy via SFTP auf IONOS `/deploy/stefankoelle/` +- CV-Partial zentral pflegbar (einmal aendern → Web + PDF aktualisieren) +- LED Matrix als eigene Seite unter stefankoelle.de/ledmatrix/ ## CI/CD -`.github/workflows/build-deploy.yml` baut alle 5 Sites (hub, infra, smarthome, code, retro) und deployt sie zu Cloudflare Pages. +### Cloudflare Pages (moonweb) +`.github/workflows/build-deploy.yml` baut hub, infra, smarthome, code, retro. Benötigte Secrets: - `CLOUDFLARE_API_TOKEN` - `CLOUDFLARE_ACCOUNT_ID` +### IONOS SFTP (stefankoelle.de) +`.github/workflows/deploy-stefankoelle.yml` baut stefankoelle.de und deployed per SFTP. + +Benötigte Secrets: +- `IONOS_SFTP_HOST` +- `IONOS_SFTP_USER` +- `IONOS_SFTP_PASSWORD` ## Offene Punkte - retro/ ist bewusst rudimentär gehalten -- Einige Cards in smarthome/ und infra/ verlinken noch auf "#" (Placeholder) -- Deploy erfolgt noch nicht automatisch (Phase 3 ausstehend) -- stefankoelle.de ist extern gehostet, wird nur verlinkt (nicht im Monorepo) +- PDF-Build fuer CV (WeasyPrint) — lokal testen, spaeter in CI integrieren +- Querverlinkungen stefankoelle.de <-> smarthome (zukuenftig) +- Ledmatrix ggf. nach smarthome verschieben (when ready) diff --git a/TODO.md b/TODO.md index 029bf22..9825c17 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,6 @@ # TODO — moonweb-site Offene Items -Stand: 2026-08-13 +Stand: 2026-08-14 ## Aktueller Status @@ -10,20 +10,34 @@ Stand: 2026-08-13 | Phase 1 — Content | ✅ fertig | hub, code, infra, smarthome komplett, retro bewusst rudimentär | | Phase 2 — CI/CD | ✅ fertig | build-deploy.yml mit Caching + Validation + Summary | | Phase 3 — Launch | ✅ fertig | Cloudflare Pages live, Smoke-Test bestanden | -| Phase 4 — Deferred | ⏸️ zurückgestellt | Ledmatrix-Migration, 28k8, Analytics | +| Phase 4 — Deferred | ⏸️ zurückgestellt | 28k8, Analytics | +| Phase 5 — stefankoelle.de | ✅ fertig | Ins Monorepo migriert, Eleventy-Build, SFTP-Deployment | ## Offene Items ### P4 — Deferred (nicht dringend) -- [ ] Ledmatrix-Migration nach smarthome - [ ] 28k8.moonweb.org Zukunft klären - [ ] Perplexity-Rückkanal - [ ] Apex-Domain Redirect (moonweb.org → hub.moonweb.org) -- [ ] stefankoelle.de in Monorepo portieren +- [ ] PDF-Build fuer CV (WeasyPrint, lokal testen) +- [ ] Querverlinkungen stefankoelle.de <-> smarthome +- [ ] Ledmatrix ggf. nach smarthome verschieben -## Schon erledigt (Stand 2026-08-13) +## Schon erledigt (Stand 2026-08-14) +### stefankoelle.de Migration (2026-08-14) +- [x] Eleventy-Config + package.json erweitert +- [x] Onepager-Layout mit Anchor-Links beibehalten +- [x] CV-Partial (zentral fuer Web + PDF) +- [x] CSS ausgelagert + modernisiert (Custom Properties, box-sizing) +- [x] Assets in assets/ verschoben +- [x] Ledmatrix als eigene Seite integriert +- [x] Alte Dateien geloescht (index.html, browserconfig.xml, sitemap.txt) +- [x] Deployment-Workflow fuer IONOS SFTP +- [x] Cloudflare-Workflow um stefankoelle erweitert (paths-ignore) + +### Vorherige Erledigungen (Stand 2026-08-13) - [x] Impressum angelegt + Footer-Link auf allen Sites - [x] Favicons (SVG mit Emojis) pro Subdomain — `shared/favicon/*.svg`, per Passthrough Copy - [x] Meta descriptions auf allen 33 Seiten @@ -51,3 +65,7 @@ Alle 5 Sites live auf Cloudflare Pages: - ✅ smarthome.moonweb.org — Smart Home Übersicht + Detailseiten - ✅ infra.moonweb.org — Infra-Übersicht (redigiert) - ✅ retro.moonweb.org — Ehrliche minimale Übersicht (WIP) + +stefankoelle.de live auf IONOS (SFTP-Deployment): +- ✅ stefankoelle.de — Onepager mit CV, Projects, Languages, Impressum +- ✅ stefankoelle.de/ledmatrix/ — LED Matrix WebServer Dokumentation diff --git a/package.json b/package.json index fe93e5e..b984688 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,15 @@ "dev:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js --serve --port=8083", "dev:code": "npx @11ty/eleventy --config=code/eleventy.config.js --serve --port=8084", "dev:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js --serve --port=8085", - "dev": "npm run dev:hub & npm run dev:infra & npm run dev:smarthome & npm run dev:code & npm run dev:retro", + "dev:stefankoelle": "npx @11ty/eleventy --config=stefankoelle/eleventy.config.js --serve --port=8086", + "dev": "npm run dev:hub & npm run dev:infra & npm run dev:smarthome & npm run dev:code & npm run dev:retro & npm run dev:stefankoelle", "build:hub": "npx @11ty/eleventy --config=hub/eleventy.config.js", "build:infra": "npx @11ty/eleventy --config=infra/eleventy.config.js", "build:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js", "build:code": "npx @11ty/eleventy --config=code/eleventy.config.js", "build:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js", - "build": "npm run build:hub && npm run build:infra && npm run build:smarthome && npm run build:code && npm run build:retro" + "build:stefankoelle": "npx @11ty/eleventy --config=stefankoelle/eleventy.config.js", + "build": "npm run build:hub && npm run build:infra && npm run build:smarthome && npm run build:code && npm run build:retro && npm run build:stefankoelle" }, "devDependencies": { "@11ty/eleventy": "^3.1.6" diff --git a/stefankoelle/_includes/cv-content.njk b/stefankoelle/_includes/cv-content.njk new file mode 100644 index 0000000..c72c241 --- /dev/null +++ b/stefankoelle/_includes/cv-content.njk @@ -0,0 +1,97 @@ +
+

CV

+ +
+

Stefan Kölle

+

Senior Software Developer & Software Architect
+ Neumarkter Str. 86c, 81673 München
+ E-Mail: [email protected]

+
+ +
+

Profile

+

Experienced Senior Software Developer and Software Architect with over 20 years of experience in developing and managing microservices and IT architectures. Specialized in .NET Core and Kubernetes, with hands-on experience in AI-assisted development using Claude Code. Clear focus on efficient, innovative, and sustainable solutions. Highly skilled in executing complex projects, building cloud-native services, and optimizing processes through modern technologies.

+
+ +
+

Core Technologies

+ +
+ +

Senior Software Developer / Senior Software Architect

+

TENHIL GmbH & Co. KG (2025 - present)

+ + +

TENHIL GmbH & Co. KG (08/2022 - 2024)

+ + +

Senior Software Developer / Senior Software Architect

+

stellenanzeigen.de GmbH & Co. KG (11/2018 - 07/2022)

+ + +

Managing Director

+

Regio-Jobanzeiger GmbH & Co. KG (10/2014 - 12/2017)

+ +

Managing Director, CTO

+

stellenanzeigen.de GmbH & Co. KG (01/2011 - 10/2018)

+ + +

CTO, Member of Executive Management

+

stellenanzeigen.de GmbH & Co. KG (01/2004 - 12/2010)

+ + +

Head of IT / Development

+

stellenanzeigen.de GmbH & Co. KG (06/2000 - 12/2003)

+ + +

Web Developer

+

IXOS AG (05/1999 - 05/2000)

+ + +

Education

+

FH Augsburg - Computer Science (1996 - 1999)

+ +

Internships

+

IXOS AG - Working Student (10/1997 - 04/1999)

+ +
diff --git a/stefankoelle/_includes/stefankoelle.njk b/stefankoelle/_includes/stefankoelle.njk new file mode 100644 index 0000000..1f3b251 --- /dev/null +++ b/stefankoelle/_includes/stefankoelle.njk @@ -0,0 +1,87 @@ + + + + + + + + {% if title %}{{ title }}{% else %}Stefan Koelle{% endif %} + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + +
+ + {{ content | safe }} + +
+
+ + + + + diff --git a/stefankoelle/assets/android-icon-144x144.png b/stefankoelle/assets/android-icon-144x144.png new file mode 100755 index 0000000..196c0a5 Binary files /dev/null and b/stefankoelle/assets/android-icon-144x144.png differ diff --git a/stefankoelle/assets/android-icon-192x192.png b/stefankoelle/assets/android-icon-192x192.png new file mode 100755 index 0000000..993b8c9 Binary files /dev/null and b/stefankoelle/assets/android-icon-192x192.png differ diff --git a/stefankoelle/assets/android-icon-36x36.png b/stefankoelle/assets/android-icon-36x36.png new file mode 100755 index 0000000..582eca9 Binary files /dev/null and b/stefankoelle/assets/android-icon-36x36.png differ diff --git a/stefankoelle/assets/android-icon-48x48.png b/stefankoelle/assets/android-icon-48x48.png new file mode 100755 index 0000000..57438e6 Binary files /dev/null and b/stefankoelle/assets/android-icon-48x48.png differ diff --git a/stefankoelle/assets/android-icon-72x72.png b/stefankoelle/assets/android-icon-72x72.png new file mode 100755 index 0000000..db036c2 Binary files /dev/null and b/stefankoelle/assets/android-icon-72x72.png differ diff --git a/stefankoelle/assets/android-icon-96x96.png b/stefankoelle/assets/android-icon-96x96.png new file mode 100755 index 0000000..3e1ffe1 Binary files /dev/null and b/stefankoelle/assets/android-icon-96x96.png differ diff --git a/stefankoelle/assets/apple-icon-114x114.png b/stefankoelle/assets/apple-icon-114x114.png new file mode 100755 index 0000000..91fe4a8 Binary files /dev/null and b/stefankoelle/assets/apple-icon-114x114.png differ diff --git a/stefankoelle/assets/apple-icon-120x120.png b/stefankoelle/assets/apple-icon-120x120.png new file mode 100755 index 0000000..8686d9e Binary files /dev/null and b/stefankoelle/assets/apple-icon-120x120.png differ diff --git a/stefankoelle/assets/apple-icon-144x144.png b/stefankoelle/assets/apple-icon-144x144.png new file mode 100755 index 0000000..196c0a5 Binary files /dev/null and b/stefankoelle/assets/apple-icon-144x144.png differ diff --git a/stefankoelle/assets/apple-icon-152x152.png b/stefankoelle/assets/apple-icon-152x152.png new file mode 100755 index 0000000..4150129 Binary files /dev/null and b/stefankoelle/assets/apple-icon-152x152.png differ diff --git a/stefankoelle/assets/apple-icon-180x180.png b/stefankoelle/assets/apple-icon-180x180.png new file mode 100755 index 0000000..4bea384 Binary files /dev/null and b/stefankoelle/assets/apple-icon-180x180.png differ diff --git a/stefankoelle/assets/apple-icon-57x57.png b/stefankoelle/assets/apple-icon-57x57.png new file mode 100755 index 0000000..7a889aa Binary files /dev/null and b/stefankoelle/assets/apple-icon-57x57.png differ diff --git a/stefankoelle/assets/apple-icon-60x60.png b/stefankoelle/assets/apple-icon-60x60.png new file mode 100755 index 0000000..de30334 Binary files /dev/null and b/stefankoelle/assets/apple-icon-60x60.png differ diff --git a/stefankoelle/assets/apple-icon-72x72.png b/stefankoelle/assets/apple-icon-72x72.png new file mode 100755 index 0000000..db036c2 Binary files /dev/null and b/stefankoelle/assets/apple-icon-72x72.png differ diff --git a/stefankoelle/assets/apple-icon-76x76.png b/stefankoelle/assets/apple-icon-76x76.png new file mode 100755 index 0000000..adad9d8 Binary files /dev/null and b/stefankoelle/assets/apple-icon-76x76.png differ diff --git a/stefankoelle/assets/apple-icon-precomposed.png b/stefankoelle/assets/apple-icon-precomposed.png new file mode 100755 index 0000000..172a814 Binary files /dev/null and b/stefankoelle/assets/apple-icon-precomposed.png differ diff --git a/stefankoelle/assets/apple-icon.png b/stefankoelle/assets/apple-icon.png new file mode 100755 index 0000000..172a814 Binary files /dev/null and b/stefankoelle/assets/apple-icon.png differ diff --git a/stefankoelle/assets/email.js b/stefankoelle/assets/email.js new file mode 100644 index 0000000..a3bc267 --- /dev/null +++ b/stefankoelle/assets/email.js @@ -0,0 +1,17 @@ +function obfuscateEmail(user, domain, tld) { + return user + '@' + domain + '.' + tld; +} +document.addEventListener('DOMContentLoaded', function() { + var emailLink = document.getElementById('email-link'); + if (emailLink) { + emailLink.href = 'mailto:' + obfuscateEmail('cv', 'stefankoelle', 'de'); + emailLink.textContent = obfuscateEmail('cv', 'stefankoelle', 'de'); + } +}); +document.addEventListener('DOMContentLoaded', function() { + var emailLink = document.getElementById('email-impressum'); + if (emailLink) { + emailLink.href = 'mailto:' + obfuscateEmail('impressum', 'stefankoelle', 'de'); + emailLink.textContent = obfuscateEmail('impressum', 'stefankoelle', 'de'); + } +}); diff --git a/stefankoelle/assets/favicon-16x16.png b/stefankoelle/assets/favicon-16x16.png new file mode 100755 index 0000000..1f2a551 Binary files /dev/null and b/stefankoelle/assets/favicon-16x16.png differ diff --git a/stefankoelle/assets/favicon-32x32.png b/stefankoelle/assets/favicon-32x32.png new file mode 100755 index 0000000..19a372a Binary files /dev/null and b/stefankoelle/assets/favicon-32x32.png differ diff --git a/stefankoelle/assets/favicon-96x96.png b/stefankoelle/assets/favicon-96x96.png new file mode 100755 index 0000000..3e1ffe1 Binary files /dev/null and b/stefankoelle/assets/favicon-96x96.png differ diff --git a/stefankoelle/assets/favicon.ico b/stefankoelle/assets/favicon.ico new file mode 100755 index 0000000..db60ab9 Binary files /dev/null and b/stefankoelle/assets/favicon.ico differ diff --git a/stefankoelle/assets/manifest.json b/stefankoelle/assets/manifest.json new file mode 100755 index 0000000..013d4a6 --- /dev/null +++ b/stefankoelle/assets/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "App", + "icons": [ + { + "src": "\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] +} \ No newline at end of file diff --git a/stefankoelle/assets/ms-icon-144x144.png b/stefankoelle/assets/ms-icon-144x144.png new file mode 100755 index 0000000..196c0a5 Binary files /dev/null and b/stefankoelle/assets/ms-icon-144x144.png differ diff --git a/stefankoelle/assets/ms-icon-150x150.png b/stefankoelle/assets/ms-icon-150x150.png new file mode 100755 index 0000000..3f1279c Binary files /dev/null and b/stefankoelle/assets/ms-icon-150x150.png differ diff --git a/stefankoelle/assets/ms-icon-310x310.png b/stefankoelle/assets/ms-icon-310x310.png new file mode 100755 index 0000000..288ebfc Binary files /dev/null and b/stefankoelle/assets/ms-icon-310x310.png differ diff --git a/stefankoelle/assets/ms-icon-70x70.png b/stefankoelle/assets/ms-icon-70x70.png new file mode 100755 index 0000000..1f32fa6 Binary files /dev/null and b/stefankoelle/assets/ms-icon-70x70.png differ diff --git a/stefankoelle/assets/stefan-koelle-foto.jpg b/stefankoelle/assets/stefan-koelle-foto.jpg new file mode 100755 index 0000000..a031e6a Binary files /dev/null and b/stefankoelle/assets/stefan-koelle-foto.jpg differ diff --git a/stefankoelle/assets/stefan-koelle-vollbild.jpg b/stefankoelle/assets/stefan-koelle-vollbild.jpg new file mode 100755 index 0000000..071a19c Binary files /dev/null and b/stefankoelle/assets/stefan-koelle-vollbild.jpg differ diff --git a/stefankoelle/assets/style.css b/stefankoelle/assets/style.css new file mode 100644 index 0000000..dbd9c47 --- /dev/null +++ b/stefankoelle/assets/style.css @@ -0,0 +1,184 @@ +/* stefankoelle.de — Modernized CSS (visually identical to original) */ + +:root { + --color-text: #665544; + --color-text-dark: #333; + --color-bg: #f4f4f4; + --color-surface: rgba(255, 255, 255, 0.9); + --color-surface-solid: #ffffff; + --color-link: #333; + --color-nav-bg: #eeeeee; + --color-shadow: rgba(0, 0, 0, 0.1); + --font-main: 'Roboto', sans-serif; + --transition: color 0.3s; +} + +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + color: var(--color-text); + font: 1em/1.6 var(--font-main); + background-color: var(--color-bg); +} + +a { color: var(--color-link); } + +h1 { font-size: 2.5em; } +h2 { font-size: 2em; margin-bottom: 10px; } +h3 { font-size: 1.5em; margin-top: 20px; margin-bottom: 10px; } + +p { margin-bottom: 10px; } + +.profile, .core-technologies, .contact-info { + margin-bottom: 20px; + max-width: 500px; +} + +/* Navigation */ +#head { display: none; } + +#links { + padding: 20px; + font-size: 1.25em; + text-align: center; + min-height: 20px; + background: var(--color-nav-bg); + position: fixed; + top: 0; + width: 100%; + z-index: 1000; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 10px; +} + +.flex-break { + flex-basis: 100%; +} + +#links a { + color: var(--color-text-dark); + text-decoration: none; + font-weight: normal; + transition: var(--transition); +} + +#links a.separator::after { + content: " | "; + margin-left: 4px; +} + +#links i.fa-brands { + margin-right: 8px; +} + +/* Sections */ +.headlinespace { + height: 560px; + padding: 80px; + position: relative; +} + +#headline { + padding: 20px; + background: var(--color-surface); + width: calc(25% - 20px); + position: relative; + left: 20px; + box-shadow: 0 2px 4px var(--color-shadow); +} + +.space { + margin-top: 1060px; + padding: 150px; + position: relative; +} + +#cvcontent, +#personalprojectscontent, +#programminglanguagescontent, +#impressumcontent { + padding: 20px; + background: var(--color-surface); + width: calc(50% - 20px); + position: relative; + left: 20px; + box-shadow: 0 2px 4px var(--color-shadow); +} + +#impressumcontent { + padding-top: 100px; + height: 200px; +} + +/* Background */ +#background-image { + position: fixed; + top: 120px; + left: 0; + width: 100%; + height: calc(100% - 120px); + background: url('/assets/stefan-koelle-vollbild.jpg') no-repeat top center fixed; + background-size: cover; + background-position: 0 120px; + z-index: -1; +} + +/* Responsive */ +@media (max-width: 1024px) { + #container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; + } + + body { + color: var(--color-text-dark); + font-size: 1em; + } + + h1 { font-size: 2em; } + h2 { font-size: 1.75em; } + h3 { font-size: 1.25em; } + + #headline, + #cvcontent, + #personalprojectscontent, + #programminglanguagescontent, + #impressumcontent { + width: calc(100% - 40px); + padding: 20px; + left: 0; + background: var(--color-surface-solid); + margin-bottom: 20px; + box-shadow: 0 2px 4px var(--color-shadow); + } + + .headlinespace { + height: 20px; + padding: 20px; + } + + .space { + margin-top: 40px; + width: 20px; + padding: 20px; + } + + #background-image { display: none; } + + #links { + padding: 20px; + background: var(--color-nav-bg); + position: relative; + width: calc(100% - 40px); + } + + #head { display: block; padding-top: 20px; } +} diff --git a/stefankoelle/cv-print.njk b/stefankoelle/cv-print.njk new file mode 100644 index 0000000..6979699 --- /dev/null +++ b/stefankoelle/cv-print.njk @@ -0,0 +1,38 @@ + + + + + + CV - Stefan Koelle + + + + +
+
+
+

Stefan Kölle

+ Senior Software Developer & Software Architect +
+ {% include "cv-content.njk" %} +
+
+ + diff --git a/stefankoelle/eleventy.config.js b/stefankoelle/eleventy.config.js new file mode 100644 index 0000000..b6d740f --- /dev/null +++ b/stefankoelle/eleventy.config.js @@ -0,0 +1,19 @@ +module.exports = function (eleventyConfig) { + eleventyConfig.addGlobalData("site", { url: "https://stefankoelle.de" }); + eleventyConfig.addFilter("date", (d) => d.toISOString()); + eleventyConfig.addPassthroughCopy({ "stefankoelle/assets": "assets" }); + eleventyConfig.addPassthroughCopy({ "stefankoelle/ledmatrix/assets": "ledmatrix/assets" }); + eleventyConfig.addPassthroughCopy({ "stefankoelle/robots.txt": "robots.txt" }); + + return { + dir: { + input: "stefankoelle", + includes: "_includes", + output: "dist/stefankoelle" + }, + serverOptions: { + host: "0.0.0.0", + port: 8086 + } + }; +}; diff --git a/stefankoelle/index.njk b/stefankoelle/index.njk new file mode 100644 index 0000000..3324c8e --- /dev/null +++ b/stefankoelle/index.njk @@ -0,0 +1,273 @@ +--- +title: "Stefan Koelle" +description: "Experienced Senior Software Developer and Software Architect with over 20 years of experience. Specialized in .NET Core and Kubernetes, with hands-on experience in AI-assisted development using Claude Code." +layout: stefankoelle.njk +--- +
+ +
+
+

Stefan Kölle

+ Senior Software Developer & Software Architect +
+ +
+ +
+{% include "cv-content.njk" %} + +
+ +
+
+

Personal Projects

+ +

Home Network Projects (2025-2026)

+ + +

Retro Computing (2022/2023)

+ + +

Upgrade of old Atari Mega 4 (2021)

+ + +

Home Automation (since 2018)

+ + +

Proxmox Server with Various LxC Linux (since 2020)

+ + +

Various Docker Systems (since 2016)

+ + +

Walk-on Song (2019)

+ + +

Kids RFID MP3 Player (2018)

+ + +

Dotnet Community Activity (2009 - 2011)

+ + +

Owner & Developer, inseco.de (2003 - 2008)

+ + +

Home-Network Projects during IT Manager Phase (2000 - 2006)

+ + +

Mailbox/Fidonet (1994 - 2000)

+ + +

PC Intro / Game / Software Development (1992 - 1994)

+ + +

Atari ST Software Development (1990 - 1992)

+ +
+ +
+ +
+
+

Programming Languages

+ +

Since 2025, AI-assisted development with Claude Code has become a core part of my workflow. This approach enables faster delivery from idea to production, better documentation artifacts, and cross-technology work across .NET, TypeScript, and Kubernetes.

+ +

.NET Core

+ + +

TypeScript

+ + +

Python

+ + +

.NET Framework

+ + +

PHP

+ + +

ASP-Classic

+ + +

Java

+ + +

C++

+ + +

PASCAL

+ + +

Assembler

+ + +

C

+ + +

BASIC

+ +
+ +
+ +
+
+

Impressum

+

Stefan Kölle
Neumarkter Str. 86c
81673 München

+

Phone/Fax: +49-(89)-20006547
E-Mail: [email protected]

+
diff --git a/stefankoelle/ledmatrix/assets/ledmatrix-az.jpg b/stefankoelle/ledmatrix/assets/ledmatrix-az.jpg new file mode 100755 index 0000000..1459d83 Binary files /dev/null and b/stefankoelle/ledmatrix/assets/ledmatrix-az.jpg differ diff --git a/stefankoelle/ledmatrix/assets/ledmatrix-blue.jpg b/stefankoelle/ledmatrix/assets/ledmatrix-blue.jpg new file mode 100755 index 0000000..c02781a Binary files /dev/null and b/stefankoelle/ledmatrix/assets/ledmatrix-blue.jpg differ diff --git a/stefankoelle/ledmatrix/assets/ledmatrix-green.jpg b/stefankoelle/ledmatrix/assets/ledmatrix-green.jpg new file mode 100755 index 0000000..73cafed Binary files /dev/null and b/stefankoelle/ledmatrix/assets/ledmatrix-green.jpg differ diff --git a/stefankoelle/ledmatrix/assets/ledmatrix-kind.jpg b/stefankoelle/ledmatrix/assets/ledmatrix-kind.jpg new file mode 100755 index 0000000..0a166e9 Binary files /dev/null and b/stefankoelle/ledmatrix/assets/ledmatrix-kind.jpg differ diff --git a/stefankoelle/ledmatrix/assets/ledmatrix-yellow.jpg b/stefankoelle/ledmatrix/assets/ledmatrix-yellow.jpg new file mode 100755 index 0000000..c2516db Binary files /dev/null and b/stefankoelle/ledmatrix/assets/ledmatrix-yellow.jpg differ diff --git a/stefankoelle/ledmatrix/assets/ledmatrix.jpg b/stefankoelle/ledmatrix/assets/ledmatrix.jpg new file mode 100755 index 0000000..63e7300 Binary files /dev/null and b/stefankoelle/ledmatrix/assets/ledmatrix.jpg differ diff --git a/stefankoelle/ledmatrix/assets/style.css b/stefankoelle/ledmatrix/assets/style.css new file mode 100644 index 0000000..e277d63 --- /dev/null +++ b/stefankoelle/ledmatrix/assets/style.css @@ -0,0 +1,416 @@ +/* LED Matrix WebServer — Standalone CSS */ + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --color-primary: #2196F3; + --color-primary-dark: #1976D2; + --color-accent: #00BCD4; + --color-success: #4CAF50; + --color-warning: #FF9800; + --color-error: #F44336; + + --color-bg: #f5f7fa; + --color-surface: #ffffff; + --color-text: #333333; + + --shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.15); + + --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; + background: var(--color-bg); + color: var(--color-text); + line-height: 1.6; +} + +/* Header & Navigation */ +header { + background: var(--color-surface); + box-shadow: var(--shadow); + position: sticky; + top: 0; + z-index: 100; +} + +nav { + max-width: 1200px; + margin: 0 auto; + padding: 1rem 2rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-size: 1.5rem; + font-weight: 700; + color: var(--color-primary); + text-decoration: none; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.nav-menu { + display: flex; + gap: 2rem; + list-style: none; + align-items: center; +} + +.nav-menu a { + color: var(--color-text); + text-decoration: none; + font-weight: 500; + transition: var(--transition); + position: relative; +} + +.nav-menu a::after { + content: ''; + position: absolute; + bottom: -4px; + left: 0; + width: 0; + height: 2px; + background: var(--color-primary); + transition: var(--transition); +} + +.nav-menu a:hover::after { + width: 100%; +} + +/* Hero Section */ +.hero { + background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); + color: white; + padding: 4rem 2rem; + text-align: center; +} + +.hero-content { + max-width: 1200px; + margin: 0 auto; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3rem; + align-items: center; +} + +.hero-text h1 { + font-size: 2.5rem; + margin-bottom: 1rem; + line-height: 1.2; +} + +.hero-text p { + font-size: 1.1rem; + margin-bottom: 2rem; + opacity: 0.95; +} + +.version-badge { + display: inline-block; + background: rgba(255, 255, 255, 0.2); + padding: 0.5rem 1rem; + border-radius: 20px; + font-size: 0.9rem; + margin-bottom: 1rem; + border: 1px solid rgba(255, 255, 255, 0.3); +} + +.cta-buttons { + display: flex; + gap: 1rem; + justify-content: flex-start; +} + +.btn { + padding: 0.75rem 1.5rem; + border-radius: 6px; + border: none; + font-weight: 600; + cursor: pointer; + transition: var(--transition); + text-decoration: none; + display: inline-block; + font-size: 1rem; +} + +.btn-primary { + background: white; + color: var(--color-primary); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); +} + +.btn-secondary { + background: rgba(255, 255, 255, 0.2); + color: white; + border: 2px solid white; +} + +.btn-secondary:hover { + background: white; + color: var(--color-primary); +} + +.hero-image { + text-align: center; +} + +.hero-image img { + max-width: 100%; + height: auto; +} + +@media (max-width: 768px) { + .hero-content { + grid-template-columns: 1fr; + } + .hero-text h1 { + font-size: 1.8rem; + } + .cta-buttons { + flex-direction: column; + } +} + +/* Main Content */ +main { + max-width: 1200px; + margin: 0 auto; + padding: 3rem 2rem; +} + +.section { + margin-bottom: 3rem; +} + +.section-title { + font-size: 2rem; + margin-bottom: 1.5rem; + color: var(--color-primary); + display: flex; + align-items: center; + gap: 0.5rem; +} + +.section-title::before { + content: ''; + width: 4px; + height: 2rem; + background: var(--color-primary); + border-radius: 2px; +} + +/* Cards */ +.cards-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 2rem; + margin-bottom: 2rem; +} + +.card { + background: var(--color-surface); + border-radius: 8px; + padding: 1.5rem; + box-shadow: var(--shadow); + transition: var(--transition); + border-left: 4px solid var(--color-primary); +} + +.card:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-lg); +} + +.card-title { + font-size: 1.2rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: var(--color-primary); +} + +.card-subtitle { + font-size: 0.85rem; + color: var(--color-accent); + margin-bottom: 1rem; +} + +.card-content { + font-size: 0.95rem; + line-height: 1.6; +} + +/* Features */ +.features { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; +} + +.feature { + display: flex; + gap: 1rem; +} + +.feature-icon { + font-size: 2rem; + color: var(--color-primary); + flex-shrink: 0; +} + +.feature-text h3 { + margin-bottom: 0.5rem; + color: var(--color-primary); +} + +.feature-text p { + font-size: 0.95rem; +} + +/* Specs Table */ +.specs-table { + background: var(--color-surface); + border-radius: 8px; + overflow: hidden; + box-shadow: var(--shadow); + margin-bottom: 2rem; +} + +.specs-table table { + width: 100%; + border-collapse: collapse; +} + +.specs-table th { + background: var(--color-primary); + color: white; + padding: 1rem; + text-align: left; + font-weight: 600; +} + +.specs-table td { + padding: 1rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} + +.specs-table tr:last-child td { + border-bottom: none; +} + +.specs-table tr:nth-child(even) { + background: rgba(33, 150, 243, 0.02); +} + +/* Code Block */ +.code-block { + background: var(--color-surface); + border-left: 4px solid var(--color-accent); + padding: 1.5rem; + border-radius: 6px; + overflow-x: auto; + margin: 1.5rem 0; + box-shadow: var(--shadow); +} + +.code-block pre { + margin: 0; + font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace; + font-size: 0.9rem; + line-height: 1.5; +} + +.code-label { + font-size: 0.85rem; + color: var(--color-accent); + font-weight: 600; + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 1px; +} + +/* Footer */ +footer { + background: var(--color-surface); + border-top: 1px solid rgba(0, 0, 0, 0.1); + padding: 3rem 2rem; + margin-top: 4rem; +} + +.footer-content { + max-width: 1200px; + margin: 0 auto; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; + margin-bottom: 2rem; +} + +.footer-section h4 { + margin-bottom: 1rem; + color: var(--color-primary); +} + +.footer-section ul { + list-style: none; +} + +.footer-section a { + color: var(--color-text); + text-decoration: none; + opacity: 0.8; + transition: var(--transition); +} + +.footer-section a:hover { + color: var(--color-primary); + opacity: 1; +} + +.footer-bottom { + text-align: center; + padding-top: 2rem; + border-top: 1px solid rgba(0, 0, 0, 0.1); + opacity: 0.7; +} + +/* Responsive */ +@media (max-width: 768px) { + nav { + flex-direction: column; + gap: 1rem; + } + + .nav-menu { + flex-direction: column; + gap: 1rem; + } + + main { + padding: 2rem 1rem; + } + + .section-title { + font-size: 1.5rem; + } + + .hero-text h1 { + font-size: 2rem; + } +} diff --git a/stefankoelle/ledmatrix/index.njk b/stefankoelle/ledmatrix/index.njk new file mode 100644 index 0000000..6983773 --- /dev/null +++ b/stefankoelle/ledmatrix/index.njk @@ -0,0 +1,562 @@ +--- +title: "LED Matrix WebServer" +description: "WiFi-controlled LED display solution with REST API, DHT22 sensor, and OTA updates." +--- + + + + + + + + {{ title }} - ESP32 Projekt + + + +
+ +
+ +
+
+
+
v1.08 (DHT) • January 2026
+

LED Matrix WebServer

+

WiFi-controlled LED display solution with REST API, DHT22 sensor, and OTA updates for your home network.

+ +
+
+

📊

+
+
+
+ +
+
+

Overview

+

+ The LED Matrix WebServer system is a fully configurable LED display solution for home networks. + It is based on the NodeMCU-32S ESP32 with up to 12 cascaded MAX7219 8x8 LED modules. + Using a simple REST API, messages can be displayed, animations controlled, and sensor data queried. +

+ +
+
+
🎯 Easy to use
+
Web-based control
+
Control your LED matrix via HTTP REST API from anywhere in the network
+
+
+
🌐 WiFi integrated
+
Fritz!Box Mesh compatible
+
Automatic WiFi connection and reconnect
+
+
+
📡 OTA updates
+
Update firmware wirelessly
+
Update your firmware without a USB connection
+
+
+
🌡️ Sensors
+
DHT22 integrated
+
Read temperature and humidity
+
+
+
🔊 Buzzer sounds
+
Tone sequences
+
Predefined melodies for alarms and notifications
+
+
+
📦 Extensible
+
Analog inputs
+
GPIO 34/35/36 available for external sensors
+
+
+
+ +
+

Hardware Components

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentModel / TypeFeatures
MicrocontrollerNodeMCU-32S ESP3232-Bit Dual-Core 240MHz, WiFi + Bluetooth
LED Display12× MAX7219 8x8 Modulecascaded
SensorDHT22 (AM2302)Temperature ±0.5°C, Humidity ±2%
BuzzerPassive BuzzerGPIO 16 PWM controlled via NPN transistor
Power SupplyUSB-C oder externe 5V PSU5V for MAX7219, 3.3V internal for logic
+
+ +

Pin Configuration

+
+
GPIO Pin Assignment
+
LedMatrix Software V1.08
+NodeMCU-32S ESP32 (USB powered)
+
+POWER SUPPLY:
+  Micro USB ──→ ESP32 (5V regulator generates 3.3V internal)
+
+SPI SIGNALS (MAX7219):
+  VIN 5V  ──→ MAX7219 VCC
+  GND     ──→ MAX7219 GND
+  GPIO 5  ──→ MAX7219 CS  (Chip Select)
+  GPIO 18 ──→ MAX7219 CLK (Clock)
+  GPIO 23 ──→ MAX7219 DIN (Data In)
+  Modul 1 DOUT → Modul 2 DIN → Modul 3 DIN
+
+DHT22 SENSOR:
+  GPIO 27 ──→ DHT22 Data
+  3V3    ──→ DHT22 VCC
+  GND     ──→ DHT22 GND
+
+BUZZER:
+  GPIO 16 ──→ Buzzer Control (via transistor)
+  GPIO 16 ──1kΩ── NPN Transistor BASE
+                    NPN Transistor COLLECTOR ──→ Buzzer +
+                    NPN Transistor EMITTER ──→ GND
+  Buzzer - ──→ GND (ESP32)
+
+ANALOG INPUTS (optional):
+  GPIO 34 ──→ Analog Sensor 1
+  GPIO 35 ──→ Analog Sensor 2
+  GPIO 36 ──→ Analog Sensor 3
+
+
+ +
+

Installation & Setup

+

+ Here are photos of various LED Matrix installations in the home network. The systems show different colors and configurations. +

+ +
+
+ Red LED Matrix Installation +
+

Red Installation

+

LED Matrix with red backlight on iMac

+
+
+ +
+ LED Matrix above AnyCubic Printer +
+

AnyCubic Printer Setup

+

LED Matrix installation above 3D printer

+
+
+ +
+ Green LED Matrix Installation +
+

Green-yellow Installation

+

LED Matrix with green/yellow LED configuration

+
+
+ +
+ Green LED Matrix Close-up +
+

Green Close-up

+

Detailed view of a green LED Matrix with wiring

+
+
+ +
+ Blue LED Matrix Installation +
+

Blue Installation

+

LED Matrix with blue brightness
Many pixels defective

+
+
+ +
+ Cyan LED Matrix Installation +
+

Cyan Installation

+

Multiple modules with cyan-colored lighting
Some pixels defective

+
+
+
+
+ +
+

Feature Overview

+ +
+
+
📱
+
+

REST API

+

HTTP-based control of all functions from anywhere in the network

+
+
+
+
🎬
+
+

Animations

+

MD_Parola Library with various scroll modes

+
+
+
+
💾
+
+

EEPROM Storage

+

Persistently save and load messages

+
+
+
+
🔄
+
+

Asynchronous Web Engine

+

Non-blocking AsyncWebServer for reliable performance

+
+
+
+
🌡️
+
+

Sensor Integration

+

DHT22 read every 60 seconds

+
+
+
+
🔊
+
+

Sounds & Alarms

+

4 predefined melodies: Error, Done, Warning, Completed

+
+
+
+
+ +
+

REST API Documentation

+ +

+ All endpoints are accessible via HTTP. The LED Matrix is recognized as ledmatrix.fritz.box in the network. +

+ +

Base Endpoints

+ +
+
Welcome Page HTML
+
GET /
+Response: HTML Welcome Page with menu
+
+ +
+
Firmware-Version
+
GET /version
+Response: v1.08 (dht)
+
+ +

Message & Display

+ +
+
Display message (static)
+
GET /print?text=Hallo%20Welt
+Displays a static message on the LED matrix (max 75 characters)
+
+ +
+
Message with scroll animation
+
GET /scroll?text=Scrolling%20Text
+Text scrolls from left to right across the matrix
+Switches back to the last print message afterwards
+
+ +
+
Test display
+
GET /test
+Displays "Hello world" on the LED matrix
+
+ +
+
Change LED brightness
+
GET /set?intensity=8
+Value: 0 (dark) to 15 (bright)
+
+ +
+
Turn off display
+
GET /off
+Turns off the LED matrix (power saving)
+
+ +

Sensor Data

+ +
+
Read temperature
+
GET /temperature
+Response: "23.5" (degrees Celsius)
+
+ +
+
Read humidity
+
GET /humidity
+Response: "65.2" (percent relative humidity)
+
+ +
+
Read analog inputs
+
GET /analog34
+GET /analog35
+GET /analog36
+Response: Value between 0-4095
+
+GET /analogvalues
+Response: "1234,2345,3456" (comma-separated values)
+
+ +

Sounds & Buzzer

+ +
+
Success melody (Done)
+
GET /sound/erledigt
+Plays positive tone sequence
+
+ +
+
Warnings melody
+
GET /sound/warnung
+Plays warning tone sequence
+
+ +
+
Error melody
+
GET /sound/fehler
+Plays error tone sequence
+
+ +
+
Completed melody
+
GET /sound/abgeschlossen
+Plays success tone sequence
+
+ +

Control & System

+ +
+
Restart system
+
GET /restart
+ESP32 will be restarted (with display shutdown)
+
+ +

curl Examples

+ +
+
curl: display text
+
curl "http://ledmatrix.fritz.box/print?text=Hallo%20Welt"
+
+ +
+
curl: text with scroll
+
curl "http://ledmatrix.fritz.box/scroll?text=Breaking%20News"
+
+ +
+
curl: read temperature
+
curl "http://ledmatrix.fritz.box/temperature"
+
+ +
+
curl: play sound
+
curl "http://ledmatrix.fritz.box/sound/erledigt"
+
+ +
+
curl: set brightness
+
curl "http://ledmatrix.fritz.box/set?intensity=10"
+
+
+ +
+

Technical Specifications

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyValueNotes
Firmware Versionv1.08 (DHT)January 2026
Power SupplyUSB-C 5V~500mA for logic
WiFi Standard802.11 b/g/n2.4GHz
LED Display12× 8x8 Module (96×8 Pixel)
DHT22 Range-40°C to +80°CHumidity 0-100% RH
Update MethodOTA (AsyncElegantOTA)Wireless via HTTP
DHT22 Update Interval60 secondsMinimum read interval
Message Buffer75 charactersMax. length of a message
+
+
+ +
+

Code Quality

+

+ The code has been thoroughly reviewed and classified as production-ready. +

+ +
+
+
✅ Strengths
+
+
    +
  • Robust WiFi management
  • +
  • Non-blocking Async-WebServer
  • +
  • Error handling for DHT
  • +
  • OTA updates implemented
  • +
+
+
+
+
⚠️ Potential for Improvement
+
+
    +
  • Hard-coded SSID/password
  • +
  • Expand input validation
  • +
+
+
+
+
📊 Evaluation
+
+
+ Grade: A-
+ Production ready
+ Uptime: 30-45 days normal +
+
+
+
+
+
+ + + + + + diff --git a/stefankoelle/pdf/cv-style.css b/stefankoelle/pdf/cv-style.css new file mode 100644 index 0000000..342f269 --- /dev/null +++ b/stefankoelle/pdf/cv-style.css @@ -0,0 +1,94 @@ +/* cv-style.css — Clean CV layout for PDF print */ + +:root { + --color-text: #333; + --color-heading: #1a1a1a; + --color-accent: #665544; + --color-border: #ddd; + --font-main: 'Roboto', sans-serif; +} + +@page { + size: A4; + margin: 20mm; +} + +body { + font-family: var(--font-main); + font-size: 10pt; + line-height: 1.5; + color: var(--color-text); +} + +h1 { + font-size: 22pt; + color: var(--color-heading); + margin-bottom: 4pt; + border: none; +} + +h2 { + font-size: 14pt; + color: var(--color-accent); + border-bottom: 2px solid var(--color-accent); + padding-bottom: 4pt; + margin-top: 16pt; + margin-bottom: 8pt; + border-left: none; +} + +h3 { + font-size: 11pt; + color: var(--color-heading); + margin-top: 10pt; + margin-bottom: 4pt; +} + +p { margin-bottom: 6pt; } + +ul { + margin: 4pt 0 8pt 16pt; + padding: 0; +} + +li { + margin-bottom: 3pt; +} + +a { + color: var(--color-text); + text-decoration: none; +} + +.contact-info { + margin-bottom: 12pt; +} + +.profile, .core-technologies { + margin-bottom: 12pt; +} + +/* Hide non-CV elements when generating PDF */ +#links, #head, #background-image, +.headlinespace, .space, +#personalprojectscontent, +#programminglanguagescontent, +#impressumcontent { + display: none !important; +} + +#headline { + position: static; + width: 100%; + box-shadow: none; + background: none; + padding: 0 0 12pt 0; +} + +#cvcontent { + position: static; + width: 100%; + box-shadow: none; + background: none; + padding: 0; +} diff --git a/stefankoelle/robots.txt b/stefankoelle/robots.txt new file mode 100644 index 0000000..b7f7f4c --- /dev/null +++ b/stefankoelle/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +Allow: / +Disallow: /deploy/ + +Sitemap: https://stefankoelle.de/sitemap.xml