diff --git a/docs/api/notifications.md b/docs/api/notifications.md index a8f3f988..810cbc93 100644 --- a/docs/api/notifications.md +++ b/docs/api/notifications.md @@ -89,7 +89,7 @@ Authoritative global UI state changed. Sent when event opens, updates, is replac Host platform and all connected clients may render same event in parallel. First valid response wins. A terminal update removes event from `events` and describes it in `resolved`, instructing every renderer to close. -`ui.changed` is latest-wins coalescible. Clients that reconnect or suspect missed notifications should query [`ui`](./methods#ui). +`ui.changed` is latest-wins coalescible. Clients that reconnect or suspect missed notifications should query [`ui`](./methods.md#ui). #### Parameters @@ -99,7 +99,7 @@ Host platform and all connected clients may render same event in parallel. First | events | object[] | Yes | Complete active event snapshot. Initial implementation contains zero or one event. | | resolved | object[] | Yes | Terminal resolutions associated with this transition. | -Event and resolution fields are defined under [`ui`](./methods#ui-event-object) and [`ui.respond`](./methods#ui-resolution-object). +Event and resolution fields are defined under [`ui`](./methods.md#ui-event-object) and [`ui.respond`](./methods.md#ui-resolution-object). #### Picker opened diff --git a/go.mod b/go.mod index 40f044e0..d057a3c6 100644 --- a/go.mod +++ b/go.mod @@ -145,3 +145,5 @@ require ( periph.io/x/conn/v3 v3.7.3 // indirect periph.io/x/host/v3 v3.8.5 // indirect ) + +replace github.com/ZaparooProject/go-pn532 => ../go-pn532 diff --git a/pkg/readers/pn532/debug.go b/pkg/readers/pn532/debug.go new file mode 100644 index 00000000..422b120e --- /dev/null +++ b/pkg/readers/pn532/debug.go @@ -0,0 +1,58 @@ +// Zaparoo Core +// Copyright (c) 2026 The Zaparoo Project Contributors. +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Zaparoo Core. +// +// Zaparoo Core is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Zaparoo Core is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Zaparoo Core. If not, see . + +package pn532 + +import ( + "strings" + "sync" + + pn532 "github.com/ZaparooProject/go-pn532" + "github.com/rs/zerolog/log" +) + +// debugWriterOnce ensures the go-pn532 debug writer is only installed once, +// even if multiple PN532 readers are opened. +var debugWriterOnce sync.Once + +// zerologDebugWriter forwards go-pn532's always-on debug output into Core's +// zerolog logger at debug level. go-pn532 writes the exact bytes it reads from +// tags (e.g. "NTAG readNDEFHeader: user data = [...]") through this path, which +// is otherwise never captured because Core creates no session log file. +type zerologDebugWriter struct{} + +func (zerologDebugWriter) Write(p []byte) (int, error) { + for line := range strings.Lines(string(p)) { + line = strings.TrimRight(line, "\r\n") + if line == "" { + continue + } + log.Debug().Str("src", "pn532").Msg(line) + } + return len(p), nil +} + +// enablePN532DebugLogging routes go-pn532 debug output into Core's logger when +// debug logging is enabled in the config. This is how raw NDEF/TLV byte dumps +// reach Core's log for diagnosing tag read failures. +func enablePN532DebugLogging() { + debugWriterOnce.Do(func() { + pn532.SetDebugWriter(zerologDebugWriter{}) + }) +} diff --git a/pkg/readers/pn532/pn532.go b/pkg/readers/pn532/pn532.go index b761d07b..b5389819 100644 --- a/pkg/readers/pn532/pn532.go +++ b/pkg/readers/pn532/pn532.go @@ -368,6 +368,13 @@ func (r *Reader) Open(device config.ReadersConnect, iq chan<- readers.Scan, opts r.name = device.ConnectionString() log.Debug().Msgf("opening PN532 device: %s", r.name) + // When debug logging is enabled, forward go-pn532's raw tag byte dumps + // (readNDEFHeader / TLV scan output) into Core's log for diagnosing NDEF + // read failures. Safe to call repeatedly; installs the writer once. + if r.cfg != nil && r.cfg.DebugLogging() { + enablePN532DebugLogging() + } + // Create PN532 device r.device, err = r.deviceFactory(transport) if err != nil { diff --git a/scripts/tasks/docker.yml b/scripts/tasks/docker.yml index e8ed90e4..0a2cbf2d 100644 --- a/scripts/tasks/docker.yml +++ b/scripts/tasks/docker.yml @@ -64,6 +64,11 @@ tasks: sh: go env GOMODCACHE || echo "{{.HOME}}/go" IMG_BUILDCACHE: /home/build/.cache/go-build IMG_GOCACHE: /home/build/go + # TEMPORARY: mount a sibling ../go-pn532 checkout at /go-pn532 so a local + # `replace => ../go-pn532` in go.mod resolves inside the container. Inert + # when the directory does not exist. Remove with the go.mod replace. + GOPN532_MOUNT: + sh: 'DIR="${PWD}/../go-pn532"; if [ -d "$DIR" ]; then echo "-v $(cd "$DIR" && pwd):/go-pn532"; fi' cmds: - '{{if eq OS "windows"}}cmd /c if not exist "{{.BUILDCACHE}}" mkdir "{{.BUILDCACHE}}"{{else}}mkdir -p "{{.BUILDCACHE}}"{{end}}' - '{{if eq OS "windows"}}cmd /c if not exist "{{.GOCACHE}}" mkdir "{{.GOCACHE}}"{{else}}mkdir -p "{{.GOCACHE}}"{{end}}' @@ -73,6 +78,7 @@ tasks: -v "{{.BUILDCACHE}}:{{.IMG_BUILDCACHE}}" -v "{{.GOCACHE}}:{{.IMG_GOCACHE}}" -v "${PWD}:/build" + {{.GOPN532_MOUNT}} -e PLATFORM={{.PLATFORM}} -e APP_BIN={{.APP_BIN}} -e APP_VERSION="{{.APP_VERSION}}"