Add bambu-nfc dump parse, Key-A HKDF, and spool merge.

Parse filanfc-dump/v1 zip/JSON, Bambu 1K images, and merge stickers by tray UID. dump and mifare packages stay importable for a later common library.
This commit is contained in:
2026-08-23 15:23:11 -05:00
parent a794db4838
commit b38f7f643b
29 changed files with 893 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
package bambunfc
import (
"fmt"
"slices"
)
// Spool is one physical filament roll (both stickers merged).
type Spool struct {
Brand string
TrayUID string
ChipUIDs []string
Type string
Material string
MaterialID string
VariantID string
Color Color
WeightG int
DiameterMM float32
LengthM int
MinHotendC int
MaxHotendC int
BedC int
DryC int
DryHours int
Produced string
SpoolWidthRaw uint16
}
func spoolKey(t Tag) string {
return t.Brand + "\x00" + t.TrayUID
}
func sameFilament(a, b Tag) bool {
return a.Type == b.Type &&
a.Material == b.Material &&
a.Color == b.Color &&
a.WeightG == b.WeightG
}
func tagToSpool(t Tag) Spool {
return Spool{
Brand: t.Brand,
TrayUID: t.TrayUID,
ChipUIDs: []string{t.ChipUID},
Type: t.Type,
Material: t.Material,
MaterialID: t.MaterialID,
VariantID: t.VariantID,
Color: t.Color,
WeightG: t.WeightG,
DiameterMM: t.DiameterMM,
LengthM: t.LengthM,
MinHotendC: t.MinHotendC,
MaxHotendC: t.MaxHotendC,
BedC: t.BedC,
DryC: t.DryC,
DryHours: t.DryHours,
Produced: t.Produced,
SpoolWidthRaw: t.SpoolWidthRaw,
}
}
// Merge groups tags by brand + tray UID into spools.
func Merge(tags []Tag) ([]Spool, error) {
order := make([]string, 0)
groups := make(map[string][]Tag)
for _, t := range tags {
if t.TrayUID == "" {
return nil, fmt.Errorf("%w: empty tray uid chip %s", ErrIncomplete, t.ChipUID)
}
k := spoolKey(t)
if _, ok := groups[k]; !ok {
order = append(order, k)
}
groups[k] = append(groups[k], t)
}
spools := make([]Spool, 0, len(order))
for _, k := range order {
g := groups[k]
s := tagToSpool(g[0])
uids := map[string]struct{}{g[0].ChipUID: {}}
for _, t := range g[1:] {
if !sameFilament(g[0], t) {
return nil, fmt.Errorf("%w: tray %s", ErrConflict, t.TrayUID)
}
uids[t.ChipUID] = struct{}{}
}
s.ChipUIDs = make([]string, 0, len(uids))
for u := range uids {
s.ChipUIDs = append(s.ChipUIDs, u)
}
slices.Sort(s.ChipUIDs)
spools = append(spools, s)
}
slices.SortFunc(spools, func(a, b Spool) int {
if a.TrayUID < b.TrayUID {
return -1
}
if a.TrayUID > b.TrayUID {
return 1
}
return 0
})
return spools, nil
}