You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: RFID-tag syncing across a central server + ESPuino peers (P2P)
New RfidSync module keeps RFID-tag assignments in sync with a central PHP server
and other ESPuinos simultaneously:
- on learn: push the freshly assigned card to the server AND every configured peer
- full sync: pull the server list, merge newest-wins per id (by timestamp), push all
local tags back to server + peers. Trigger: button in the Sync settings tab,
bindable command 187 (CMD_RFID_SYNC), or MQTT topic rfid_sync.
- catch-up: one automatic full sync after coming online, so a player used offline/
outdoors (no WiFi/server) keeps working and uploads its offline-learned cards on
reconnect. Cards learned while the clock was never set (ts=0) are healed to 'now'
on the first online sync so they win the merge.
- timestamps stored in a parallel NVS namespace (the tag string must stay 4 tokens).
- the /rfid POST endpoint is the peer-push target: accepts an incoming 'timestamp'
and does NOT re-push (no sync loops).
Wire format matches the (existing) server: {id, timestamp, fileOrUrl, playMode} or
{id, timestamp, modId}. Server is the dynamic server/manifest.php (also serves the
file-sync manifest; GET ?type=rfid returns just the tag list, POST merges newest-wins).
Settings (Sync tab): RFID server URL, peer list, push-on-learn toggle, full-sync
button, and a link to manifest.php for self-hosting.
Also in this change:
- fix: restore the file-browser main tab label to Files/Dateien/Fichiers (the
rfid-settings-tab commit had clobbered nav.rfid); the RFID settings sub-tab now
uses nav.rfidcards.
- fix: listening-time stats were invisible in the info dialog for durations under a
minute (timestampToDuration with withSeconds=false returns '' -> row skipped); the
daily stats now render with seconds.
Verified end-to-end against the real server (espuinosync.locn.de / SWAG): full sync
merged+pushed 3 cards with correct playMode/modId mapping; learn pushed a card to the
server; peer-target /rfid stored an entry with its incoming timestamp; test data
cleaned up afterwards.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: README.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -140,6 +140,7 @@ neon logo that doubles as the SVG favicon ([`7be5254`](../../commit/7be5254)):
140
140
|**Auto-save settings (General + Buttons)**: the **General** and **Buttons** settings sub-tabs no longer have a submit button — changes are persisted automatically (debounced ~0.6 s) as soon as you toggle/select or leave a field. Populating the form on load is suppressed so it never triggers a spurious save; the **Reset** button now applies the defaults right away. The credential/connection tabs (WiFi, security, MQTT, FTP, Bluetooth) keep their explicit submit button on purpose (half-typed passwords / reboot on Bluetooth-mode change). i18n de/en/fr |[`settings-autosave`](../../releases/tag/settings-autosave)|
141
141
|**RFID settings sub-tab + learned-cards list**: the old inline RFID-assignment form (id field + play-mode + submit) is gone — music cards are learned from the file-browser context menu (scan popup). A new **RFID** settings sub-tab learns **modification cards** (pick a modification action → **Learn card** → scan popup) and lists **all learned cards** (music = path + play mode, modification = action) with a delete button each. i18n de/en/fr |[`rfid-settings-tab`](../../releases/tag/rfid-settings-tab)|
142
142
|**Listening-time statistics**: tracks how long was actually listened per local calendar day in a 365-day ring buffer (persisted in NVS, ~1.5 KB). The info dialog (ℹ️) shows **today / yesterday / last 7 days / last 30 days** next to the existing total playtime. Seconds are accumulated by the audio loop while playing (clock from NTP/RTC; ignored until the clock is valid), flushed to NVS at most once a minute and on shutdown. New `/info` audio fields `playToday`/`playYesterday`/`play7d`/`play30d` (seconds). i18n de/en/fr |[`listening-stats`](../../releases/tag/listening-stats)|
143
+
| **RFID-tag syncing (server + peer-to-peer)**: keeps RFID-tag assignments in sync across a central PHP server *and* other ESPuinos at the same time. On **learn** a freshly assigned card is pushed to the server and to every configured peer; a **full sync** (button in the Sync settings tab, bindable command **187**, or MQTT `rfid_sync`) pulls the server list, merges newest-wins per tag id (by timestamp) and pushes every local tag back to server + peers. A **catch-up** sync runs automatically once after the device comes online, so a player used **offline/outdoors** (no WiFi, no server) keeps working — cards learned offline are stored locally and pushed up on reconnect (assignments made while the clock was never set are healed to "now" on first sync so they win). Conflict resolution is **newest-wins** by timestamp. Server endpoint is the dynamic [`server/manifest.php`](server/manifest.php) (same script that serves the file-sync manifest; `?type=rfid` returns just the tag list); peers are a comma-separated list of other ESPuino hosts in the Sync settings tab. i18n de/en/fr | [`rfid-sync`](../../releases/tag/rfid-sync) |
143
144
|**OpenAPI/Swagger spec updated**: the bundled `REST_API.yaml` (served at `/swagger.html`) now documents all fork-added endpoints (`/sync`, `/syncstop`, `/playlist`, `/githubupdate`, `/version`, `/login`, `/logout`, `/security`, `/eqrule(s)`, `/activeequalizer`, `/bluetooth*`, `/sdclean`) and declares the security schemes (`X-API-Key` header / `apikey` query param, or the `ESPUINO_SESSION` login cookie) so the Swagger "Authorize" button works. The web-interface footer links to the Swagger UI (`/swagger.html`) |[`f8298d9`](../../commit/f8298d9)|
Copy file name to clipboardExpand all lines: html/locales/de.json
+19-2Lines changed: 19 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -30,7 +30,7 @@
30
30
"updateAvailable": "Update verfügbar",
31
31
"clickToUpdate": "zum Installieren klicken",
32
32
"control": "Steuerung",
33
-
"rfid": "RFID",
33
+
"rfid": "Dateien",
34
34
"wifi": "WLAN",
35
35
"mqtt": "MQTT",
36
36
"ftp": "FTP",
@@ -40,7 +40,8 @@
40
40
"settings": "Einstellungen",
41
41
"buttons": "Tasten",
42
42
"tools": "Tools",
43
-
"help": "Hilfe"
43
+
"help": "Hilfe",
44
+
"rfidcards": "RFID"
44
45
},
45
46
"settingsex": {
46
47
"title": "Erweiterte Einstellungen",
@@ -450,6 +451,22 @@
450
451
"done": "Synchronisation abgeschlossen",
451
452
"failed": "Synchronisation fehlgeschlagen",
452
453
"stopped": "Synchronisation gestoppt"
454
+
},
455
+
"rfid": {
456
+
"title": "RFID-Karten-Sync",
457
+
"desc": "Synchronisiert RFID-Karten-Zuordnungen mit einem zentralen Server (manifest.php) und/oder anderen ESPuinos (P2P). Bei Konflikt gewinnt die zuletzt geänderte Zuordnung.",
Copy file name to clipboardExpand all lines: html/locales/en.json
+19-2Lines changed: 19 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -30,7 +30,7 @@
30
30
"updateAvailable": "Update available",
31
31
"clickToUpdate": "click to install",
32
32
"control": "Control",
33
-
"rfid": "RFID",
33
+
"rfid": "Files",
34
34
"wifi": "WiFi",
35
35
"mqtt": "MQTT",
36
36
"ftp": "FTP",
@@ -40,7 +40,8 @@
40
40
"settings": "Settings",
41
41
"buttons": "Buttons",
42
42
"tools": "Tools",
43
-
"help": "Help"
43
+
"help": "Help",
44
+
"rfidcards": "RFID"
44
45
},
45
46
"settingsex": {
46
47
"title": "Advanced settings",
@@ -450,6 +451,22 @@
450
451
"done": "Synchronization complete",
451
452
"failed": "Synchronization failed",
452
453
"stopped": "Synchronization stopped"
454
+
},
455
+
"rfid": {
456
+
"title": "RFID card sync",
457
+
"desc": "Syncs RFID card assignments with a central server (manifest.php) and/or other ESPuinos (P2P). On conflict the most recently changed assignment wins.",
Copy file name to clipboardExpand all lines: html/locales/fr.json
+19-2Lines changed: 19 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -30,7 +30,7 @@
30
30
"updateAvailable": "Mise à jour disponible",
31
31
"clickToUpdate": "cliquer pour installer",
32
32
"control": "Contrôle",
33
-
"rfid": "RFID",
33
+
"rfid": "Fichiers",
34
34
"wifi": "WiFi",
35
35
"mqtt": "MQTT",
36
36
"ftp": "FTP",
@@ -40,7 +40,8 @@
40
40
"settings": "Paramètres",
41
41
"buttons": "Boutons",
42
42
"tools": "Outils",
43
-
"help": "Aide"
43
+
"help": "Aide",
44
+
"rfidcards": "RFID"
44
45
},
45
46
"settingsex": {
46
47
"title": "Paramètres avancés",
@@ -443,6 +444,22 @@
443
444
"done": "Synchronisation terminée",
444
445
"failed": "Échec de la synchronisation",
445
446
"stopped": "Synchronisation arrêtée"
447
+
},
448
+
"rfid": {
449
+
"title": "Sync des cartes RFID",
450
+
"desc": "Synchronise les associations de cartes RFID avec un serveur central (manifest.php) et/ou d'autres ESPuinos (P2P). En cas de conflit, l'association la plus récente l'emporte.",
0 commit comments