# Sort2Light — Changelog

## v39

### Nowe funkcje
- **Tryb Pick2Light (P2L)** — urządzenie może pracować jako Sort2Light lub Pick2Light; wybór trybu w UI (toggle S2L/P2L)
- **Layouty wózka P2L** — 9 konfigurowalnych layoutów mapowania koszyków→diody, zapisywanych osobno w Preferences
- **Endpoint `GET /pick?baskets=1,2`** — zapala diody koszyków wg aktywnego layoutu i akcji "Wskazanie koszyka"; każde wywołanie resetuje poprzednie
- **Endpoint `GET /pick/reset`** — gasi wszystkie diody P2L
- **Endpoint `GET /p2l/layout?idx=N`** i **`POST /p2l/layout?idx=N`** — odczyt i zapis layoutu
- **Akcja "Wskazanie koszyka"** — konfigurowalny blink (kolor, tempo, mrugnięcia) używany przez `/pick`
- **QR kod na LCD** — w trybie P2L wyświetla QR z adresem IP urządzenia do rejestracji w WMS
- **Hostname urządzenia** — dynamiczny, spójny po LAN i WiFi: `S2L-83E81F84` (LAN) / `S2L-E8834DF0` (WiFi); widoczny w routerze i w UI
- **Hostname w `/status`** — endpoint zwraca pole `hostname` używane przez UI do budowania URL z nazwą urządzenia
- **Dwa URL do integracji w UI** — po IP i po nazwie urządzenia (hostname), oba z przyciskiem Kopiuj
- **Dokumentacja** — `index.html`, `Sort2Light_Guide.html`, `CHANGELOG.md` na serwerze

### Poprawki
- Fix: `setBetaChannel` wywoływało `save()` podczas `loadConfig` → nadpisywało config z `null` w tempo/count zanim `setAction` zdążyło ustawić wartości (główna przyczyna białych kolorów i zerowych wartości akcji)
- Fix: domyślny JSON w `GET /config` uzupełniony o `device_mode`, `beta_channel`, `storages`, `single_onepcs`, `single_multipcs`
- Fix: `setDeviceMode` i `setBetaChannel` nie zapisują podczas ładowania — `doSave` tylko przy kliknięciu przez użytkownika

---


## v38

### Nowe funkcje
- **WiFi fallback** — gdy brak kabla LAN, urządzenie próbuje połączyć się z zapisaną siecią WiFi; jeśli brak credentials lub połączenie nie powiedzie się → tryb AP
- **Tryb AP (Access Point)** — urządzenie tworzy sieć `Sort2Light-XXXX`, strona na `192.168.4.1` umożliwia wybór sieci WiFi z listy (skanowanie) i wpisanie hasła
- **Reset WiFi przez btnC przy starcie** — trzymaj btnC podczas bootowania → kasuje credentials WiFi → restart → tryb AP
- **Reset WiFi z UI** — przycisk `⚠ Reset WiFi` w panelu wywołuje `GET /wifi-reset` → restart → tryb AP
- **OTA przez WiFi** — `checkFirmwareUpdate` używa `WiFiClient` lub `EthernetClient` zależnie od aktywnego połączenia
- **Nowe endpointy**: `GET /wifi-reset`, `GET /wifi-status`
- **Toggle STABLE/BETA** — kanał aktualizacji zmieniony z checkboxa na przycisk dwustanowy STABLE/BETA
- **Komunikaty OTA na LCD** — wszystkie stany OTA wyświetlane na ekranie przez 3s: `Firmware aktualny`, `OTA: brak sieci`, `OTA: brak polaczenia`, `OTA: timeout`, `OTA: blad serwera` itd.

### Poprawki
- Fix: `delay(2500)` po `LAN.init()` — W5500 potrzebuje czasu po resecie żeby PHY wykryło kabel; bez tego `linkStatus()` zwracał `LinkOFF` mimo podłączonego kabla
- Fix: HTTP przez WiFi — dodano `WiFiServer wifiServer(80)`; interfejs `Client*` pozwala jednemu kodowi obsługiwać Ethernet i WiFi

---


## v37

### Nowe funkcje
- **Demo sekwencji akcji** — state machine odpalająca wszystkie akcje kolejno: `cancelled → nolocation → single → single_multipcs → location[każda półka] → lastproduct[każda półka]`
- Endpoint `GET /testdemo` uruchamia demo
- Przycisk **▶▶ Test akcji** w UI (obok Test Wave)
- Długie przyciśnięcie `btnA` (2s) uruchamia demo (zamiast poprzedniego rainbow testu)
- **Redirect** z `/` na `/ui` — wejście na samo IP przekierowuje na panel
- **`parseIds` rozszerzone** — obsługuje zakresy (`1-5` → `1,2,3,4,5`) oraz dodatkowe separatory: `;`, `.`, spacja

### Poprawki
- Fix: zapis akcji systemowych i mapowania lokalizacji — flush TCP bufora zjadał body POST
- Fix: `test()` i `testRow()` używają aktualnych wartości z UI bez konieczności wcześniejszego zapisania
- Fix: `window.onload` sekwencyjne (`await loadConfig` → `await loadStatus`) — naprawa problemu z ładowaniem w Chrome
- Fix: `Connection: close` + `Content-Length` dla `/config` i `/status` — naprawa ładowania na przeglądarkach mobilnych
- Fix: parsowanie `ids=` w `/testaction` — poprzednio brało wszystko do końca stringa łącznie z kolejnymi parametrami

---

## v36

### Nowe funkcje
- **Kanał beta OTA** — pole `beta_channel` w configu, checkbox w UI, żółty badge **BETA** przy wersji firmware; urządzenie odpytuje `beta.json` lub `latest.json` zależnie od ustawienia
- **Custom dropdown kolorów** — zamiast zwykłego `<select>` kolorowy kwadracik przy każdej opcji na liście; lista renderowana jako portal (nie przycina się przez `overflow:hidden` tabeli)
- **Limit prądowy LED** — `FastLED.setMaxPowerInVoltsAndMilliamps(5, 450)` — automatyczne skalowanie jasności gdy pobór prądu przekracza limit

### Poprawki
- Fix: flush TCP bufora nie zjada body POST — zapis konfiguracji działał tylko dla `ledcount` i `controller`, teraz zapisuje też `system`, `storages`, `actions`
- Fix: `Content-Length` w odpowiedzi `/ui` — naprawa problemu "tylko OK" przy szybkim odświeżaniu strony
- Fix: pętla czytania body POST z timeoutem — dane TCP mogą przychodzić w paczkach

---

## v35
_Punkt startowy — pierwsza wersja stabilna wdrożona u klientów_

### Stan na v35
- Obsługa LAN (W5500 SPI) z auto-reconnect
- Endpointy HTTP: `/onscan`, `/on`, `/off`, `/blink`, `/stop`, `/find`, `/reset`, `/identify`, `/status`, `/version`, `/ui`, `/config`, `/setleds`, `/checkupdate`, `/resetconfig`
- Logika `/onscan`: `cancelled`, `nolocation`, `single`, `single_multipcs`, `location`, `lastproduct`
- Blink engine nieblokujący (millis-based)
- OTA przez Ethernet
- UI konfiguracyjny (dark/cyan, logo EasyStorage)
- NTP zegar (porzucony w v34, nie weszło do v35)
- `btnA` krótko → find/stop, `btnA` długo → rainbow test
- `btnB` / `btnC` → zmiana liczby aktywnych LEDów

---

## v33
_Poprzednia wersja stabilna_

### Poprawki względem v32
- Fix: konflikt nazwy zmiennej `String leds` w bloku `/onscan` z globalną `CRGB leds[MAX_LEDS]` → przemianowano na `String storageLeds`

---

## v32
_Wersja referencyjna klienta_

### Poprawki względem v30
- Fix: `logt()` używana przed definicją → dodano forward declarations
- Fix: `server.begin()` nie wywoływany przy starcie gdy brak LAN
- Fix: `LAN.begin()` z `Ethernet.linkStatus() == LinkON` — unika 60s blokady DHCP gdy brak kabla
- Fix: błędne IP na LCD gdy LAN niedostępny
- Fix: HTTP read timeout 50ms → 300ms, zmieniono warunek na `client.connected()`

---

## Planowane (backlog)

- ~~**WiFi**~~ — zrealizowane w v38
- **Pick2Light** — osobny projekt po wdrożeniu S2L u klientów
- Aktualizacja nazw zmiennych `{{...}}` w URL template po potwierdzeniu przez zespół EasyStorage
