package bambunfc import ( "bytes" "encoding/binary" "encoding/hex" "fmt" "math" "git.s1d3sw1ped.com/s1d3sw1ped/bambu-nfc/mifare" ) const BrandBambuLab = "Bambu Lab" // Tag is one Bambu sticker parsed from a raw MIFARE Classic 1K image. type Tag struct { Brand string ChipUID string // lowercase hex of the ISO UID TrayUID string // 32 lowercase hex chars (block 9) 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 block(image []byte, n int) []byte { off := n * mifare.BlockSize return image[off : off+mifare.BlockSize] } func cstr(b []byte) string { if i := bytes.IndexByte(b, 0); i >= 0 { b = b[:i] } return string(b) } func allZero(b []byte) bool { for _, v := range b { if v != 0 { return false } } return true } // Parse reads a Bambu tag from raw NFC: ISO chip UID and the 1024-byte Classic 1K image. func Parse(uid, image []byte) (Tag, error) { var zero Tag if len(uid) == 0 { return zero, fmt.Errorf("%w: empty", ErrUID) } if len(image) != mifare.Size1K { return zero, fmt.Errorf("%w: want %d got %d", ErrImageSize, mifare.Size1K, len(image)) } b1 := block(image, 1) b2 := block(image, 2) b4 := block(image, 4) b5 := block(image, 5) b6 := block(image, 6) b9 := block(image, 9) b10 := block(image, 10) b12 := block(image, 12) b14 := block(image, 14) material := cstr(b2) typ := cstr(b4) if material == "" && typ == "" { return zero, ErrNotBambu } if allZero(b9) { return zero, fmt.Errorf("%w: tray uid", ErrIncomplete) } return Tag{ Brand: BrandBambuLab, ChipUID: hex.EncodeToString(uid), VariantID: cstr(b1[0:8]), MaterialID: cstr(b1[8:16]), Material: material, Type: typ, Color: Color{ R: b5[0], G: b5[1], B: b5[2], A: b5[3], }, WeightG: int(binary.LittleEndian.Uint16(b5[4:6])), DiameterMM: math.Float32frombits(binary.LittleEndian.Uint32(b5[8:12])), TrayUID: hex.EncodeToString(b9), DryC: int(binary.LittleEndian.Uint16(b6[0:2])), DryHours: int(binary.LittleEndian.Uint16(b6[2:4])), BedC: int(binary.LittleEndian.Uint16(b6[6:8])), MaxHotendC: int(binary.LittleEndian.Uint16(b6[8:10])), MinHotendC: int(binary.LittleEndian.Uint16(b6[10:12])), SpoolWidthRaw: binary.LittleEndian.Uint16(b10[4:6]), Produced: cstr(b12), LengthM: int(binary.LittleEndian.Uint16(b14[4:6])), }, nil }