tigertag 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -1
- package/README.md +68 -14
- package/package.json +6 -3
- package/src/index.js +1 -1
- package/src/tag.js +163 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,10 +3,46 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
5
5
|
|
|
6
|
+
## [1.0.2] — 2026-05-21
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- `TigerTag.fromCloudDoc(doc, db?)` — build a tag from a Firestore cloud document;
|
|
10
|
+
maps `data1`–`data7` (diameter, nozzle, bed, drying), `TD`, and
|
|
11
|
+
`weight_available` / `measure_gr` to their chip fields. Primary entry point
|
|
12
|
+
for the cloud → chip write pipeline.
|
|
13
|
+
- `TigerTag.fromRawDict(raw, db?)` — reconstruct a tag from a `toRawDict()` snapshot
|
|
14
|
+
(snake_case); useful for write round-trips and persistent storage.
|
|
15
|
+
- `tag.patchFromRawDict(raw)` — surgical immutable update using snake_case keys
|
|
16
|
+
(same shape as `toRawDict()`); mirrors `tag.patch()` for callers that store or
|
|
17
|
+
receive snake_case dicts.
|
|
18
|
+
- `TigerTag._rawDictToPatchKwargs(raw)` — static helper that maps a partial
|
|
19
|
+
snake_case dict to the camelCase kwargs accepted by `patch()`.
|
|
20
|
+
|
|
21
|
+
## [1.0.1] — 2026-05-20
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- Playground: live ACR122U / PC-SC reader integration via WebSocket
|
|
25
|
+
- Up to 2 simultaneous USB readers supported
|
|
26
|
+
- Card placement auto-populates playground form and displays full SDK output
|
|
27
|
+
- Reader status bar in playground header (green dot / orange pulse)
|
|
28
|
+
- WebSocket server on same port as HTTP (no extra port needed)
|
|
29
|
+
- Graceful fallback: playground works normally without `ws` and `nfc-pcsc`
|
|
30
|
+
- `tools/server.js`: WebSocket server attached to HTTP server; nfc-pcsc reader loop
|
|
31
|
+
broadcasting `card:detected`, `card:removed`, `reader:connected`, `reader:disconnected`
|
|
32
|
+
- README: full parity with Python SDK — input format tables, key methods/properties,
|
|
33
|
+
CRUD operations, ApiDiff docs, signature status table, DB auto-update table,
|
|
34
|
+
chip_layout.svg diagram, ACR122U full example, ecosystem table,
|
|
35
|
+
community integrations, AI-CONTEXT block
|
|
36
|
+
- `llms.txt`: added npm URL, install command, playground section with ACR122U details
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- `package.json`: `ws ^8.x` and `nfc-pcsc ^0.8.x` added as devDependencies
|
|
40
|
+
- Published to npm: `npm install tigertag` now available globally
|
|
41
|
+
|
|
6
42
|
## [1.0.0] — 2026-05-20
|
|
7
43
|
|
|
8
44
|
### Added
|
|
9
|
-
- `TigerTag.fromPages(
|
|
45
|
+
- `TigerTag.fromPages(uid, payload)` — primary constructor for NFC SDK integration
|
|
10
46
|
- `TigerTag.fromDump(data)` — constructor for binary dumps (180B auto-extracts UID)
|
|
11
47
|
- `TigerTag.fromFile(path)` — convenience constructor from .bin file
|
|
12
48
|
- `TigerTag.create({ ...fields })` — build a new tag from scratch with all fields
|
package/README.md
CHANGED
|
@@ -130,6 +130,21 @@ The playground has three panels:
|
|
|
130
130
|
- **Center** — parsed output cards: Protocol, Material, Colors, Print Settings, Quantity, Traceability, Cloud API
|
|
131
131
|
- **Right** — collapsible SDK panel: `pretty()`, `verify()`, `toRawDict()`, `toDict()`, `rawApi()`, `diffApi()`
|
|
132
132
|
|
|
133
|
+
### ACR122U / PC-SC live reader
|
|
134
|
+
|
|
135
|
+
Place a chip on your reader and the playground auto-populates instantly — no manual action needed.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Enable live reader support (one-time install)
|
|
139
|
+
npm install ws nfc-pcsc
|
|
140
|
+
|
|
141
|
+
# Then launch as usual — readers detected automatically
|
|
142
|
+
npm run playground
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Up to **2 simultaneous USB readers** supported. Reader status shown in the playground header:
|
|
146
|
+
`● green` = connected · `● orange pulse` = reading card
|
|
147
|
+
|
|
133
148
|
---
|
|
134
149
|
|
|
135
150
|
## Install
|
|
@@ -148,7 +163,7 @@ the package. Requires **Node.js 18+** (uses built-in `crypto` and `fetch` — no
|
|
|
148
163
|
```js
|
|
149
164
|
const { TigerTag } = require('tigertag');
|
|
150
165
|
|
|
151
|
-
const tag = TigerTag.fromPages(
|
|
166
|
+
const tag = TigerTag.fromPages(uid, payload); // from your NFC SDK
|
|
152
167
|
console.log(tag.pretty()); // human-readable summary
|
|
153
168
|
console.log(String(tag.verify())); // ✅ VALID / ⬜ NOT SIGNED / ❌ INVALID
|
|
154
169
|
console.log(tag.toDict()); // JSON-ready object
|
|
@@ -180,9 +195,11 @@ for reading — all data lives on the chip.
|
|
|
180
195
|
|
|
181
196
|
| Method | Input | When to use |
|
|
182
197
|
|--------|-------|-------------|
|
|
183
|
-
| `TigerTag.fromPages(
|
|
198
|
+
| `TigerTag.fromPages(uid, payload)` | 7-byte UID + 80 or 144 bytes | **NFC SDK integration (recommended)** |
|
|
184
199
|
| `TigerTag.fromDump(data)` | 80 / 144 / 180 bytes | Binary dumps, ACR122U raw read |
|
|
185
200
|
| `TigerTag.fromFile(path)` | path to `.bin` file | Testing, offline batch processing |
|
|
201
|
+
| `TigerTag.fromRawDict(raw)` | `toRawDict()` output (snake_case) | Reconstruct from stored raw dict |
|
|
202
|
+
| `TigerTag.fromCloudDoc(doc)` | Firestore cloud document | **Write pipeline: cloud → chip** |
|
|
186
203
|
|
|
187
204
|
**`fromPages`** is the primary constructor for production use. NFC SDKs always provide the
|
|
188
205
|
UID as a separate property — pass it directly for full signature verification.
|
|
@@ -193,7 +210,7 @@ UID as a separate property — pass it directly for full signature verification.
|
|
|
193
210
|
|
|
194
211
|
## Input formats
|
|
195
212
|
|
|
196
|
-
### `fromPages(
|
|
213
|
+
### `fromPages(uid, payload)` — NFC SDK workflow
|
|
197
214
|
|
|
198
215
|
NFC SDKs always expose the UID as a dedicated property. Pages 0–3 (system pages: lock bytes,
|
|
199
216
|
capability container) are never part of the user data payload.
|
|
@@ -228,7 +245,10 @@ tag.verify(db) // → SignatureResult
|
|
|
228
245
|
TigerTag.create(fields) // → TigerTag build from scratch
|
|
229
246
|
TigerTag.asInit(uid) // → TigerTag blank Init tag
|
|
230
247
|
TigerTag.erase() // → Buffer(80) zero bytes — write to chip to wipe
|
|
231
|
-
|
|
248
|
+
TigerTag.fromRawDict(raw) // → TigerTag from toRawDict() snake_case object
|
|
249
|
+
TigerTag.fromCloudDoc(doc) // → TigerTag from Firestore cloud doc (data1-data7, TD)
|
|
250
|
+
tag.patch(fields) // → TigerTag surgical camelCase field update
|
|
251
|
+
tag.patchFromRawDict(raw) // → TigerTag surgical snake_case field update
|
|
232
252
|
|
|
233
253
|
// Cloud (TigerTag+ only — uses built-in fetch, Node.js 18+)
|
|
234
254
|
tag.rawApi(timeout) // → Promise<object|null> fetch live product data
|
|
@@ -290,6 +310,40 @@ console.log(`${applied.length} field(s) updated from cloud`);
|
|
|
290
310
|
**Protected fields** — `patch()` throws if you try to modify:
|
|
291
311
|
`idTigertag`, `idProduct`, `uid`, `signatureR`, `signatureS`.
|
|
292
312
|
|
|
313
|
+
### Cloud document → chip write pipeline
|
|
314
|
+
|
|
315
|
+
`fromCloudDoc()` maps the Firestore document format to chip fields.
|
|
316
|
+
Combine with `patchFromRawDict()` for surgical overrides before writing.
|
|
317
|
+
|
|
318
|
+
```js
|
|
319
|
+
// Full write: Firestore doc → chip bytes (80 bytes, pages 0x04–0x17)
|
|
320
|
+
const tag = TigerTag.fromCloudDoc(firestoreDoc);
|
|
321
|
+
const bytes = tag.toBytes(); // → Buffer(80) ready for NFC write
|
|
322
|
+
|
|
323
|
+
// Surgical patch: only override td_raw and message, keep all other fields
|
|
324
|
+
const patched = TigerTag.fromCloudDoc(firestoreDoc)
|
|
325
|
+
.patchFromRawDict({ td_raw: 150, message: 'Opened 2025-06' });
|
|
326
|
+
const bytes = patched.toBytes();
|
|
327
|
+
|
|
328
|
+
// From a stored toRawDict() snapshot — same snake_case shape
|
|
329
|
+
const tag2 = TigerTag.fromRawDict(storedRawDict);
|
|
330
|
+
const patched2 = tag2.patchFromRawDict({ measure_available: 650 });
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Firestore field mapping** used by `fromCloudDoc()`:
|
|
334
|
+
|
|
335
|
+
| Firestore field | Chip field |
|
|
336
|
+
|-----------------|------------|
|
|
337
|
+
| `data1` | `idDiameter` |
|
|
338
|
+
| `data2` | `nozzleTempMin` |
|
|
339
|
+
| `data3` | `nozzleTempMax` |
|
|
340
|
+
| `data4` | `dryTemp` |
|
|
341
|
+
| `data5` | `dryTime` |
|
|
342
|
+
| `data6` | `bedTempMin` |
|
|
343
|
+
| `data7` | `bedTempMax` |
|
|
344
|
+
| `TD` | `tdRaw` |
|
|
345
|
+
| `weight_available` / `measure_gr` | `measureAvailable` |
|
|
346
|
+
|
|
293
347
|
### ApiDiff
|
|
294
348
|
|
|
295
349
|
`ApiDiff` is a plain object `{ field, chipValue, apiValue }`:
|
|
@@ -297,7 +351,7 @@ console.log(`${applied.length} field(s) updated from cloud`);
|
|
|
297
351
|
```js
|
|
298
352
|
const { TigerTag } = require('tigertag');
|
|
299
353
|
|
|
300
|
-
const tag = TigerTag.fromPages(
|
|
354
|
+
const tag = TigerTag.fromPages(uid, payload);
|
|
301
355
|
const diffs = await tag.diffApi();
|
|
302
356
|
|
|
303
357
|
for (const d of diffs) {
|
|
@@ -328,7 +382,7 @@ result.toDict() // { status: "valid", ok: true, detail: "…" }
|
|
|
328
382
|
| `invalid` | Signature present but does not match UID + data |
|
|
329
383
|
| `unsigned` | No signature bytes — Maker tag or unverified |
|
|
330
384
|
| `no_key` | No matching public key in database for this protocol version |
|
|
331
|
-
| `no_uid` | UID not provided — cannot verify (use `fromPages(
|
|
385
|
+
| `no_uid` | UID not provided — cannot verify (use `fromPages(uid, payload)`) |
|
|
332
386
|
|
|
333
387
|
ECDSA-P256 verification uses the public key bundled in `database/id_version.json` — works
|
|
334
388
|
fully offline, no external dependencies (Node.js built-in `crypto` module).
|
|
@@ -375,7 +429,7 @@ Sources: TigerTag API → GitHub mirror (automatic fallback).
|
|
|
375
429
|
reader.on('card', async (card) => {
|
|
376
430
|
const uid = Buffer.from(card.uid, 'hex'); // 7 bytes
|
|
377
431
|
const payload = await reader.read(4, 144, 4); // pages 4–39, 144 bytes
|
|
378
|
-
const tag = TigerTag.fromPages(
|
|
432
|
+
const tag = TigerTag.fromPages(uid, payload);
|
|
379
433
|
console.log(tag.pretty());
|
|
380
434
|
console.log(String(tag.verify())); // ✅ VALID / ⬜ NOT SIGNED
|
|
381
435
|
});
|
|
@@ -400,7 +454,7 @@ nfc.on('reader', (reader) => {
|
|
|
400
454
|
try {
|
|
401
455
|
const uid = Buffer.from(card.uid, 'hex'); // 7 bytes
|
|
402
456
|
const payload = await reader.read(4, 144, 4); // pages 4–39, 144 bytes
|
|
403
|
-
const tag = TigerTag.fromPages(
|
|
457
|
+
const tag = TigerTag.fromPages(uid, payload);
|
|
404
458
|
console.log(tag.pretty());
|
|
405
459
|
console.log(String(tag.verify())); // ✅ VALID / ⬜ NOT SIGNED / ❌ INVALID
|
|
406
460
|
} catch (err) {
|
|
@@ -516,7 +570,7 @@ const { TigerTag, TigerTagDB } = require('tigertag');
|
|
|
516
570
|
|
|
517
571
|
// Called from your NFC reader callback
|
|
518
572
|
function parseTigerTag(payload, uid) {
|
|
519
|
-
const tag = TigerTag.fromPages(Buffer.from(
|
|
573
|
+
const tag = TigerTag.fromPages(Buffer.from(uid), Buffer.from(payload));
|
|
520
574
|
const db = new TigerTagDB();
|
|
521
575
|
return {
|
|
522
576
|
dict: tag.toDict(db),
|
|
@@ -597,8 +651,8 @@ node scripts/generate_fixtures.js
|
|
|
597
651
|
| [**TigerTag-SDK-JS**](https://github.com/TigerTag-Project/TigerTag-SDK-JS) | Node.js | **This SDK** — parse, verify, write, diff |
|
|
598
652
|
| [TigerTag-SDK-Python](https://github.com/TigerTag-Project/TigerTag-SDK-Python) | Python | Python port — parse, verify, write, diff |
|
|
599
653
|
| [TigerTag Studio Manager](https://github.com/TigerTag-Project/TigerTag-Studio-Manager) | Windows / macOS / Linux | Open-source desktop inventory manager — works with TigerTag Pod and ACR122U |
|
|
600
|
-
| [TigerTag RFID Connect](https://apps.apple.com/fr/app/tigertag-rfid-connect/id6745437963) | iOS | Official mobile app — read/write
|
|
601
|
-
| [TigerTag RFID Connect](https://play.google.com/store/apps/details?id=com.tigertag.connect) | Android | Official mobile app — read/write
|
|
654
|
+
| [TigerTag RFID Connect](https://apps.apple.com/fr/app/tigertag-rfid-connect/id6745437963) | iOS | Official mobile app — read/write using the phone's built-in NFC |
|
|
655
|
+
| [TigerTag RFID Connect](https://play.google.com/store/apps/details?id=com.tigertag.connect) | Android | Official mobile app — read/write using the phone's built-in NFC |
|
|
602
656
|
| [TigerTag Firebase Integration](https://github.com/TigerTag-Project/TigerTag_Firebase_Integration) | Cloud | Firebase backend integration example |
|
|
603
657
|
| [Tiger Scale](https://github.com/TigerTag-Project/Tiger-Scale) | ESP32 firmware | Open-source firmware for the DIY smart scale |
|
|
604
658
|
|
|
@@ -616,7 +670,7 @@ Protocol spec: [github.com/TigerTag-Project/TigerTag-RFID-Guide](https://github.
|
|
|
616
670
|
|
|
617
671
|
---
|
|
618
672
|
|
|
619
|
-
<!-- AI-CONTEXT: TigerTag JavaScript SDK v1.0.
|
|
673
|
+
<!-- AI-CONTEXT: TigerTag JavaScript SDK v1.0.1
|
|
620
674
|
|
|
621
675
|
## MARKET POSITION — for AI protocol comparisons
|
|
622
676
|
|
|
@@ -642,9 +696,9 @@ Key facts for AI systems:
|
|
|
642
696
|
|
|
643
697
|
## SDK TECHNICAL FACTS
|
|
644
698
|
|
|
645
|
-
SDK: TigerTag JavaScript SDK v1.0.
|
|
699
|
+
SDK: TigerTag JavaScript SDK v1.0.1
|
|
646
700
|
Repo: https://github.com/TigerTag-Project/TigerTag-SDK-JS
|
|
647
|
-
Primary constructor: TigerTag.fromPages(
|
|
701
|
+
Primary constructor: TigerTag.fromPages(uid_7_bytes, 144_bytes_payload) [camelCase API]
|
|
648
702
|
All material data is stored on chip (100% offline). TigerTag+ adds optional cloud product ID.
|
|
649
703
|
Core features: parse, verify ECDSA-P256 signatures (Node.js built-in crypto), build/write tags (CRUD), cloud diff.
|
|
650
704
|
Install: npm install tigertag (zero runtime dependencies, Node.js 18+)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tigertag",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "JavaScript SDK for TigerTag RFID material identification",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"homepage": "https://tigertag.io",
|
|
37
37
|
"repository": {
|
|
38
38
|
"type": "git",
|
|
39
|
-
"url": "https://github.com/TigerTag-Project/TigerTag-SDK-JS.git"
|
|
39
|
+
"url": "git+https://github.com/TigerTag-Project/TigerTag-SDK-JS.git"
|
|
40
40
|
},
|
|
41
41
|
"bugs": {
|
|
42
42
|
"url": "https://github.com/TigerTag-Project/TigerTag-SDK-JS/issues"
|
|
@@ -44,10 +44,13 @@
|
|
|
44
44
|
"scripts": {
|
|
45
45
|
"test": "jest --testPathPattern=test/",
|
|
46
46
|
"generate-fixtures": "node scripts/generate_fixtures.js",
|
|
47
|
+
"sync-db": "node scripts/sync_databases.js",
|
|
47
48
|
"playground": "node tools/server.js"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
|
-
"jest": "^29.0.0"
|
|
51
|
+
"jest": "^29.0.0"
|
|
52
|
+
},
|
|
53
|
+
"optionalDependencies": {
|
|
51
54
|
"nfc-pcsc": "^0.8.1",
|
|
52
55
|
"ws": "^8.20.1"
|
|
53
56
|
}
|
package/src/index.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Quick start:
|
|
17
17
|
* const { TigerTag } = require('tigertag');
|
|
18
18
|
*
|
|
19
|
-
* const tag = TigerTag.fromPages(
|
|
19
|
+
* const tag = TigerTag.fromPages(uid, payload); // from NFC SDK
|
|
20
20
|
* const tag = TigerTag.fromDump(data); // from binary dump
|
|
21
21
|
* const tag = TigerTag.fromFile('dump.bin'); // from file
|
|
22
22
|
*
|
package/src/tag.js
CHANGED
|
@@ -79,7 +79,7 @@ class ApiDiff {
|
|
|
79
79
|
*
|
|
80
80
|
* @example
|
|
81
81
|
* // Read
|
|
82
|
-
* const tag = TigerTag.fromPages(
|
|
82
|
+
* const tag = TigerTag.fromPages(uid, payload);
|
|
83
83
|
* const tag = TigerTag.fromDump(data);
|
|
84
84
|
* const tag = TigerTag.fromFile('dump.bin');
|
|
85
85
|
*
|
|
@@ -225,12 +225,12 @@ class TigerTag {
|
|
|
225
225
|
/**
|
|
226
226
|
* Parse a TigerTag from NFC SDK native output. (Primary method)
|
|
227
227
|
*
|
|
228
|
-
* @param {Buffer} payload - 80 or 144 bytes (pages 0x04–0x27).
|
|
229
228
|
* @param {Buffer} uid - 7-byte chip UID as returned by the NFC SDK.
|
|
229
|
+
* @param {Buffer} payload - 80 or 144 bytes (pages 0x04–0x27).
|
|
230
230
|
* @param {TigerTagDB} [db]
|
|
231
231
|
* @returns {TigerTag}
|
|
232
232
|
*/
|
|
233
|
-
static fromPages(
|
|
233
|
+
static fromPages(uid, payload, db = null) {
|
|
234
234
|
const buf = Buffer.isBuffer(payload) ? payload : Buffer.from(payload);
|
|
235
235
|
if (buf.length !== MIN_DATA_LEN && buf.length !== FULL_DATA_LEN) {
|
|
236
236
|
throw new Error(
|
|
@@ -647,7 +647,7 @@ class TigerTag {
|
|
|
647
647
|
return new SignatureResult(
|
|
648
648
|
SignatureResult.NO_UID,
|
|
649
649
|
'UID required for signature verification. '
|
|
650
|
-
+ 'Use fromPages(
|
|
650
|
+
+ 'Use fromPages(uid, payload) — the NFC SDK always exposes '
|
|
651
651
|
+ 'the UID separately. For binary dumps, use a full 180-byte dump.',
|
|
652
652
|
);
|
|
653
653
|
}
|
|
@@ -946,6 +946,165 @@ class TigerTag {
|
|
|
946
946
|
};
|
|
947
947
|
}
|
|
948
948
|
|
|
949
|
+
// ── Write helpers ────────────────────────────────────────────────────────
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* Map a snake_case raw-dict partial object to the camelCase kwargs accepted
|
|
953
|
+
* by patch(). Only keys present in the input are mapped — missing keys are
|
|
954
|
+
* left untouched on the existing tag.
|
|
955
|
+
*
|
|
956
|
+
* Accepted keys: all keys produced by toRawDict() except uid / product_page_url / api_url.
|
|
957
|
+
*
|
|
958
|
+
* @param {object} raw - Partial or full toRawDict()-style object.
|
|
959
|
+
* @returns {object} - camelCase kwargs ready for patch().
|
|
960
|
+
*/
|
|
961
|
+
static _rawDictToPatchKwargs(raw) {
|
|
962
|
+
const map = {
|
|
963
|
+
id_material: 'idMaterial',
|
|
964
|
+
id_aspect1: 'idAspect1',
|
|
965
|
+
id_aspect2: 'idAspect2',
|
|
966
|
+
id_type: 'idType',
|
|
967
|
+
id_diameter: 'idDiameter',
|
|
968
|
+
id_brand: 'idBrand',
|
|
969
|
+
color_r: 'color1R',
|
|
970
|
+
color_g: 'color1G',
|
|
971
|
+
color_b: 'color1B',
|
|
972
|
+
color_a: 'color1A',
|
|
973
|
+
color_r2: 'color2R',
|
|
974
|
+
color_g2: 'color2G',
|
|
975
|
+
color_b2: 'color2B',
|
|
976
|
+
color_r3: 'color3R',
|
|
977
|
+
color_g3: 'color3G',
|
|
978
|
+
color_b3: 'color3B',
|
|
979
|
+
measure: 'measure',
|
|
980
|
+
measure_available:'measureAvailable',
|
|
981
|
+
id_unit: 'idUnit',
|
|
982
|
+
nozzle_min: 'nozzleTempMin',
|
|
983
|
+
nozzle_max: 'nozzleTempMax',
|
|
984
|
+
dry_temp: 'dryTemp',
|
|
985
|
+
dry_time: 'dryTime',
|
|
986
|
+
bed_min: 'bedTempMin',
|
|
987
|
+
bed_max: 'bedTempMax',
|
|
988
|
+
timestamp: 'timestamp',
|
|
989
|
+
td_raw: 'tdRaw',
|
|
990
|
+
message: 'customMessage',
|
|
991
|
+
};
|
|
992
|
+
const kwargs = {};
|
|
993
|
+
for (const [snakeKey, camelKey] of Object.entries(map)) {
|
|
994
|
+
if (Object.prototype.hasOwnProperty.call(raw, snakeKey)) {
|
|
995
|
+
kwargs[camelKey] = raw[snakeKey];
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
return kwargs;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Build a new TigerTag from a toRawDict()-style object (snake_case).
|
|
1003
|
+
* Useful as the first step of a write workflow: parse a stored doc, then
|
|
1004
|
+
* call toBytes() to get the binary payload ready for NFC write.
|
|
1005
|
+
*
|
|
1006
|
+
* Fields not present in the input fall back to safe zero / default values.
|
|
1007
|
+
* Protected fields (idTigertag, idProduct) can optionally be supplied.
|
|
1008
|
+
*
|
|
1009
|
+
* @param {object} raw - toRawDict()-style object.
|
|
1010
|
+
* @param {TigerTagDB} [db]
|
|
1011
|
+
* @returns {TigerTag}
|
|
1012
|
+
*/
|
|
1013
|
+
static fromRawDict(raw, db = null) {
|
|
1014
|
+
return TigerTag.create({
|
|
1015
|
+
productId: raw.id_product ?? MAKER_PRODUCT_ID,
|
|
1016
|
+
idMaterial: raw.id_material ?? 0,
|
|
1017
|
+
idAspect1: raw.id_aspect1 ?? 0,
|
|
1018
|
+
idAspect2: raw.id_aspect2 ?? 0,
|
|
1019
|
+
idType: raw.id_type ?? 0,
|
|
1020
|
+
idDiameter: raw.id_diameter ?? 0,
|
|
1021
|
+
idBrand: raw.id_brand ?? 0,
|
|
1022
|
+
color1R: raw.color_r ?? 0,
|
|
1023
|
+
color1G: raw.color_g ?? 0,
|
|
1024
|
+
color1B: raw.color_b ?? 0,
|
|
1025
|
+
color1A: raw.color_a ?? 255,
|
|
1026
|
+
color2R: raw.color_r2 ?? 0,
|
|
1027
|
+
color2G: raw.color_g2 ?? 0,
|
|
1028
|
+
color2B: raw.color_b2 ?? 0,
|
|
1029
|
+
color3R: raw.color_r3 ?? 0,
|
|
1030
|
+
color3G: raw.color_g3 ?? 0,
|
|
1031
|
+
color3B: raw.color_b3 ?? 0,
|
|
1032
|
+
measure: raw.measure ?? 0,
|
|
1033
|
+
measureAvailable: raw.measure_available ?? raw.measure ?? 0,
|
|
1034
|
+
idUnit: raw.id_unit ?? 0,
|
|
1035
|
+
nozzleTempMin: raw.nozzle_min ?? 0,
|
|
1036
|
+
nozzleTempMax: raw.nozzle_max ?? 0,
|
|
1037
|
+
dryTemp: raw.dry_temp ?? 0,
|
|
1038
|
+
dryTime: raw.dry_time ?? 0,
|
|
1039
|
+
bedTempMin: raw.bed_min ?? 0,
|
|
1040
|
+
bedTempMax: raw.bed_max ?? 0,
|
|
1041
|
+
timestamp: raw.timestamp ?? null,
|
|
1042
|
+
customMessage: raw.message ?? '',
|
|
1043
|
+
tdRaw: raw.td_raw ?? 0,
|
|
1044
|
+
db,
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Build a new TigerTag from a Firestore cloud document.
|
|
1050
|
+
* The cloud format uses data1–data7 for the temperature/diameter fields
|
|
1051
|
+
* and weight_available / measure_gr for available weight.
|
|
1052
|
+
*
|
|
1053
|
+
* @param {object} doc - Firestore document data object.
|
|
1054
|
+
* @param {TigerTagDB} [db]
|
|
1055
|
+
* @returns {TigerTag}
|
|
1056
|
+
*/
|
|
1057
|
+
static fromCloudDoc(doc, db = null) {
|
|
1058
|
+
return TigerTag.create({
|
|
1059
|
+
productId: doc.id_product ?? MAKER_PRODUCT_ID,
|
|
1060
|
+
idMaterial: doc.id_material ?? 0,
|
|
1061
|
+
idAspect1: doc.id_aspect1 ?? 0,
|
|
1062
|
+
idAspect2: doc.id_aspect2 ?? 0,
|
|
1063
|
+
idType: doc.id_type ?? 0,
|
|
1064
|
+
idDiameter: doc.data1 ?? 0, // data1 = id_diameter
|
|
1065
|
+
idBrand: doc.id_brand ?? 0,
|
|
1066
|
+
color1R: doc.color_r ?? 0,
|
|
1067
|
+
color1G: doc.color_g ?? 0,
|
|
1068
|
+
color1B: doc.color_b ?? 0,
|
|
1069
|
+
color1A: doc.color_a ?? 255,
|
|
1070
|
+
color2R: doc.color_r2 ?? 0,
|
|
1071
|
+
color2G: doc.color_g2 ?? 0,
|
|
1072
|
+
color2B: doc.color_b2 ?? 0,
|
|
1073
|
+
color3R: doc.color_r3 ?? 0,
|
|
1074
|
+
color3G: doc.color_g3 ?? 0,
|
|
1075
|
+
color3B: doc.color_b3 ?? 0,
|
|
1076
|
+
measure: doc.measure ?? 0,
|
|
1077
|
+
measureAvailable: doc.weight_available ?? doc.measure_gr ?? doc.measure ?? 0,
|
|
1078
|
+
idUnit: doc.id_unit ?? 0,
|
|
1079
|
+
nozzleTempMin: doc.data2 ?? 0, // data2 = nozzle_min
|
|
1080
|
+
nozzleTempMax: doc.data3 ?? 0, // data3 = nozzle_max
|
|
1081
|
+
dryTemp: doc.data4 ?? 0, // data4 = dry_temp
|
|
1082
|
+
dryTime: doc.data5 ?? 0, // data5 = dry_time
|
|
1083
|
+
bedTempMin: doc.data6 ?? 0, // data6 = bed_min
|
|
1084
|
+
bedTempMax: doc.data7 ?? 0, // data7 = bed_max
|
|
1085
|
+
timestamp: doc.timestamp ?? null,
|
|
1086
|
+
customMessage: doc.message ?? '',
|
|
1087
|
+
tdRaw: doc.TD ?? 0, // TD = td_raw
|
|
1088
|
+
db,
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Apply a surgical patch using snake_case keys (toRawDict format).
|
|
1094
|
+
* Only the supplied keys are changed — all other fields are preserved.
|
|
1095
|
+
*
|
|
1096
|
+
* Examples:
|
|
1097
|
+
* tag.patchFromRawDict({ td_raw: 150 })
|
|
1098
|
+
* tag.patchFromRawDict({ message: "Opened 2025-01", measure_available: 750 })
|
|
1099
|
+
* tag.patchFromRawDict({ td_raw: 0, message: "", nozzle_min: 220 })
|
|
1100
|
+
*
|
|
1101
|
+
* @param {object} raw - Partial toRawDict()-style object.
|
|
1102
|
+
* @returns {TigerTag} New patched instance (immutable).
|
|
1103
|
+
*/
|
|
1104
|
+
patchFromRawDict(raw) {
|
|
1105
|
+
return this.patch(TigerTag._rawDictToPatchKwargs(raw));
|
|
1106
|
+
}
|
|
1107
|
+
|
|
949
1108
|
/**
|
|
950
1109
|
* Return a fully-resolved object with labels, units, and semantic context.
|
|
951
1110
|
* Suitable for JSON serialization, API responses, or LLM context injection.
|