Commit Graph

10 Commits

Author SHA1 Message Date
MaPaLo76
9e3e06eb09 feat(state-machine): add grace time state after free time
Implements FR-020: Grace Time (buffer period after free time)

Problem: Fan relay signal continues ~5s after laser stops, causing
false billable time charges when user stops at countdown 1 second.

Solution: New GRACE state between GRATIS and NET_COUNTING with
configurable grace period (default 5s, range 0-30s). During grace
time, display shows blinking 0 (number of blinks = remaining seconds).
If laser stops during GRACE, no billable session is created.

State machine: INACTIVE -> GRATIS -> GRACE -> NET_COUNTING

Changes:
- config.h: Add DEFAULT_GRACE_TIME_S (5), MIN/MAX constants
- settings.h/cpp: Add graceTimeSeconds field, NVS persistence
- laser_tracker.h/cpp: Add GRACE state, transition logic
- display_manager.h/cpp: Add showGraceBlinking() with blink animation
- main.cpp: Add GRACE state handling in display update loop
- mqtt_client.cpp: Add grace_time_s field to session JSON
- web_server.cpp: Add Grace Time input field in config page
- README.md: Update documentation with GRACE state details
- Feature-Requests.md: Mark FR-020 as completed (v1.6.4)

Version: 1.6.4
Tested: Manual testing with logs and MQTT Explorer
2026-05-15 15:08:15 +02:00
MaPaLo76
99b5c25e37 feat(tracker): resetSessionSum + MQTT reset_session + UI-Verbesserungen
- resetSession() umbenannt in resetSessionSum() (klarere Semantik)
- Bug fix: resetSessionSum() setzt laufende Session-Timer korrekt zurueck
  (vorher: getAllSessionsSumMinutes() blieb > 0 nach Reset)
- consumeSessionReset() nach consumeSessionEnd()-Muster (consume-Semantik)
- Vor Reset: akkumulierte Netto-Sekunden sichern -> publishSession() wie Session-Ende
- MQTT: payload {reset_session:true} via lasercutter/reset loest resetSessionSum() aus
- MQTT: Session-Reset publiziert identisches JSON wie normales Session-Ende
- Web: Button-Layout ueberarbeitet (alle 3 Buttons blau, uebereinander, gleich breit)
- Docs: README.md + Implementation-Plan.md aktualisiert
2026-02-23 22:49:30 +01:00
MaPaLo76
18a1f67f64 feat(mqtt): session_start_time ISO-8601 UTC; NTP waitForNtp nach WLAN-Connect; docs: README + Plan Phase 9 abgeschlossen 2026-02-23 21:23:45 +01:00
MaPaLo76
530d2f4670 feat(main): Phase 8 - WebServer, Watchdog, WLAN/MQTT-Fehlerstatus, Initialisierungsreihenfolge, Ceiling-Minuten pro Session 2026-02-23 20:50:04 +01:00
MaPaLo76
e273f1ea6c feat(display): showSessionRing - Sekunden-Kreisanzeige auf Modulen 5-7, 12-Uhr-Start (Phase 5b / 5.7 abgeschlossen) 2026-02-23 20:19:33 +01:00
MaPaLo76
e98002db7b refactor(laser,web): getSessionMinutes->getAllSessionsSumMinutes, Platzhalter SESSION->ALLSESSIONS, BURST->LASTSESSION 2026-02-23 19:50:47 +01:00
MaPaLo76
d644ff4986 refactor(laser): Burst-Begriff durch Session ersetzt (BurstState->SessionState, getLastBurstSeconds->getLastSessionSeconds, consumeBurstEnd->consumeSessionEnd) 2026-02-23 19:33:40 +01:00
MaPaLo76
71ef2c7ad0 feat(mqtt): Phase 6 - MqttClient implementiert (PubSubClient, publishSession, Heartbeat, Reset-Subscribe) 2026-02-22 19:36:29 +01:00
MaPaLo76
d114a58de6 fix(phase5): LaserTracker neu - Session/Burst-Logik korrigiert
- BurstState-Maschine: INACTIVE -> GRATIS -> NET_COUNTING
- _sessionNetSec (RAM): Netto-Sekunden der Betriebssitzung, reset beim Start
- _totalMinutesBase (NVS via settings.saveTotalMinutes): persistierte Gesamtzeit
- Gratis-Countdown startet bei JEDEM Laser-AN-Ereignis neu
- Netto-Zeit wird erst nach Ablauf der Gratiszeit gezaehlt
- getSessionMinutes(): sessionNetSec / 60 (ganzzahlig, hart)
- getCountdownRemaining(): nur 0 wenn INACTIVE oder NET_COUNTING
- Display Module 1-3: Session-Minuten (nicht Gesamtzeit)
- Display Module 5-7: Countdown waehrend GRATIS, sonst Idle
- settings.saveTotalMinutes() statt direkter Preferences-Calls
- main.cpp + test_laser_tracker.cpp auf neue API umgestellt
2026-02-22 18:48:35 +01:00
MaPaLo76
3827342c0c feat(laser): implement LaserTracker with debounce, gratis time and NVS save
- Add include/laser_tracker.h: LaserTracker class declaration
  - Software debounce (LASER_DEBOUNCE_MS = 50ms from config.h)
  - Polarity-aware GPIO read (LOW_ACTIVE / HIGH_ACTIVE from settings)
  - Session lifecycle: onSessionStart / onSessionEnd
  - Gratis time countdown, net seconds accumulation
  - Getters: isActive, getTotalMinutes, getSessionSeconds,
    getCountdownRemaining, getLastSessionSeconds, resetTotal

- Add src/laser_tracker.cpp: full implementation
  - readRaw() applies signal polarity
  - loop() handles debounce state machine and edge detection
  - onSessionEnd() saves updated totalMinutes to NVS
  - getTotalMinutes() returns live value (base + current net session)

- Add test_sketches/test_laser_tracker.cpp: interactive test sketch
  - Button on GPIO 4 simulates laser signal
  - BOOT button (GPIO 0, 3s hold) resets total
  - Display: modules 1-3 = total minutes, modules 5-7 = countdown/net

- Add test-laser-tracker env to platformio.ini
- Update src/main.cpp: integrate laserTracker.begin/loop + live display
- Update Implementation-Plan.md: mark Phase 5 tasks 5.1-5.6 complete
2026-02-22 14:30:14 +01:00