Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
CMF-Watch-Pro-2-BLE-Protocol — CMF Watch Pro 2 के लिए रिवर्स-इंजीनियर्ड BLE प्रोटोकॉल, GATT लेआउट, AES-128-CBC एन्क्रिप्टेड कमांड फ्रेम्स, प्रमाणीकरण हैंडशेक और वैकल्पिक साथी ऐप विकास हेतु स्वास्थ्य डेटा सिंक का दस्तावेज़ीकरण। | Kitploit
उपकरण/GitHubGitHub/joshuapassos/cmf-watch-pro-2-ble-protocol
एम्बेडेड सिस्टम सुरक्षाब्लूटूथ सुरक्षाIoT सुरक्षारिवर्स इंजीनियरिंगवायरलेस सुरक्षाक्रिप्टोग्राफीमोबाइल सुरक्षाहार्डवेयर और IoT सुरक्षाफर्मवेयर विश्लेषण

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें
GitHubjoshuapassos/cmf-watch-pro-2-ble-protocol

CMF-Watch-Pro-2-BLE-Protocol

CMF Watch Pro 2 के लिए रिवर्स-इंजीनियर्ड BLE प्रोटोकॉल, GATT लेआउट, AES-128-CBC एन्क्रिप्टेड कमांड फ्रेम्स, प्रमाणीकरण हैंडशेक और वैकल्पिक साथी ऐप विकास हेतु स्वास्थ्य डेटा सिंक का दस्तावेज़ीकरण।

रिपॉजिटरी देखेंवेबसाइट
3281 महीना पहलेअभी तक समीक्षित नहीं

CMF Watch Pro 2 — BLE प्रोटोकॉल (रिवर्स-इंजीनियर्ड)

अनौपचारिक। यह दस्तावेज़ CMF Watch Pro 2 (CMF by Nothing) के ब्लूटूथ लो एनर्जी (BLE) प्रोटोकॉल का वर्णन करता है, जिसे एक वैकल्पिक साथी ऐप के लिए रिवर्स इंजीनियरिंग द्वारा पुनर्निर्मित किया गया है। यह Nothing/CMF से संबद्ध या समर्थित नहीं है। अपने जोखिम पर उपयोग करें।

फ्रेम हेडर और ऑपकोड में सभी मल्टी-बाइट पूर्णांक बिग-एंडियन हैं। कमांड पेलोड के अंदर पूर्णांक लिटिल-एंडियन हैं जब तक कि अन्यथा न कहा गया हो (यह डिवाइस फर्मवेयर को दर्शाता है) — अपवादों पर ध्यान दें (GOALS_SET, GPS_PUSH, बल्क-ट्रांसफर ऑफसेट/लंबाई बिग-एंडियन हैं)।

विश्वास मार्कर

नीचे प्रत्येक गैर-स्पष्ट दावे को टैग किया गया है कि इसे कैसे स्थापित किया गया था:

  • ✅ डिवाइस पर मान्य — डिक्रिप्टेड लाइव कैप्चर में देखा गया या वास्तविक घड़ी के विरुद्ध परीक्षण किया गया।
  • 🔎 फर्मवेयर / APK RE से — फर्मवेयर (1.0.0.73) या आधिकारिक APK (3.5.7) को डीकंपाइल करके निकाला गया; कोड के अनुरूप लेकिन रनटाइम-परीक्षण नहीं।
  • 🟡 आंशिक रूप से सिद्ध — संरचनात्मक रूप से स्थापित (ऑफ़लाइन, कॉर्पस-वाइड, या RE द्वारा) लेकिन शेष चरण को घड़ी की आवश्यकता है और इसे चलाया नहीं गया है।
  • ⚠️ [अनिश्चित] — अनुमानित, पुष्टि नहीं; गलत हो सकता है।

जहाँ बाद का अनुभाग पहले वाले को सुधारता है, वहाँ पहले वाला पाठ हटाने के बजाय एक पॉइंटर के साथ रखा जाता है — यह जानना कि कौन से रीडिंग आज़माए गए और खारिज किए गए, अगले व्यक्ति को वही चक्कर बचाता है।

सभी कैप्चर के लिए परीक्षण डिवाइस: CMF Watch Pro 2-5485, fw 1.0.0.73, सीरियल CI04102520008192, MCU Actions ATS3089C (Cortex-M4), स्क्रीन 466×360।


1. GATT लेआउट

फ़ोन GATT क्लाइंट है; घड़ी पेरिफेरल है, जो CMF Watch Pro 2-XXXX (4 हेक्स वर्ण) के रूप में विज्ञापन करती है।

प्रत्येक CCCD (00002902-…) पर 01 00 लिखकर नोटिफिकेशन सक्षम करें। कमांड चैनल (fff1/fff2) नीचे दिए गए फ्रेम किए गए प्रोटोकॉल को ले जाता है। शेल चैनल (77d4…) सादा AT-शैली टेक्स्ट ले जाता है (जैसे AT GETSECRET; §14 देखें)। डेटा चैनल (02f0…) बड़े बाइनरी ब्लॉब्स (वॉचफेस, फर्मवेयर, AGPS) ले जाता है, जो कमांड चैनल पर नियंत्रण ऑपकोड द्वारा समन्वित होते हैं।

सेवा UUID — घड़ी ~10 प्राथमिक सेवाओं का विज्ञापन करती है। वास्तविक इकाई पर गणना की गई: 0xfff0 (कमांड), 0x180f (बैटरी), 0x180a (डिवाइस जानकारी), 0xefe7, 0xffd0, 02f00000-…ffe0 और 02f00000-…fe00 (डेटा), 77d4e67c-2fe2-2334-0d35-9ccd078f529c (शेल / पेयरिंग), e49a3001-f69a-11e8-8eb2-f2801f1b9fd1, f48a23c0-f69a-11e8-8eb2-f2801f1b9fd1।

⚠️ शेल सेवा UUID 77d4e67c-… है, न कि 77d4ff00-…। इस दस्तावेज़ के पहले के संशोधनों ने मान लिया था कि सेवा अपनी विशेषताओं (77d4ff01/77d4ff02, §14) के ff00 उपसर्ग को साझा करती है — ऐसा नहीं है, कम से कम उस इकाई पर जिस पर यह जाँचा गया था (खोज freethinkel/fmc से, §स्रोत देखें)। विशेषता UUID अपरिवर्तित हैं। यह सत्यापित नहीं है कि 77d4e67c इकाइयों में स्थिर है या नहीं — हार्डकोड करने के बजाय गणना करें।

🌐 वेब ब्लूटूथ कॉलआउट। क्रोमियम केवल उन सेवाओं को खोजता है जिन्हें पृष्ठ ने optionalServices में सूचीबद्ध किया है, यहाँ तक कि एक अनफ़िल्टर्ड getPrimaryServices() कॉल के लिए भी — 3 सेवाओं को सूचीबद्ध करने वाला पृष्ठ 3 देखता है, जबकि chrome://bluetooth-internals (क्रोम का अपना C++ लेयर, अनस्कोप्ड) सभी 10 दिखाता है। यदि आप एक ब्राउज़र क्लाइंट लिखते हैं, तो ऊपर हर UUID को पहले से सूचीबद्ध करें या पेयरिंग उन सेवाओं के साथ विफल हो जाएगी जो स्पष्ट रूप से मौजूद हैं। फ़ायरफ़ॉक्स/सफारी में कोई वेब ब्लूटूथ नहीं; उपयोगकर्ता इशारा + HTTPS/localhost की आवश्यकता है।

✅ एक पूरा वास्तविक सत्र एकल कमांड चैनल पर चला — 160 s के भारी-उपयोग कैप्चर के दौरान डेटा/फर्मवेयर या शेल चैनलों पर कोई ट्रैफ़िक नहीं था, सिवाय एक स्पष्ट OTA/वॉचफेस ट्रांसफर के।


2. फ्रेम प्रारूप (0xF5)

प्रत्येक कमांड-चैनल संदेश एक या अधिक 11-बाइट-हेडर फ्रेम में लपेटा जाता है:``` +------+-----------+--------+-------------+-------------+--------+-------------------+ | 0xF5 | chunkLen | cmd1 | chunkCount | chunkIndex | cmd2 | chunk bytes … | | 1 B | 2 B (BE) | 2 B BE | 2 B BE | 2 B BE | 2 B BE | chunkLen bytes | +------+-----------+--------+-------------+-------------+--------+-------------------+ __________________________ 11-byte header ____________________________/

root@kitploit:~
- `cmd1`/`cmd2` मिलकर **opcode** बनाते हैं (§6 देखें)। 🔎 आधिकारिक ऐप के फ्रेम बिल्डर (`C6117b.m30831g`) के विरुद्ध पुष्टि की गई।
- `chunkCount` = इस कमांड के लिए कुल चंक; `chunkIndex` **1-आधारित** है।
- `chunkLen` = इस फ्रेम में `chunk` के बाइट्स की संख्या।
- एक एकल BLE राइट लिंक MTU द्वारा खंडित हो सकती है; रिसीवर कच्चे बाइट्स को बफर करता है और पूर्ण फ्रेम्स को फिर से निकालता है। बड़े पेलोड को कई चंक्स में विभाजित किया जाता है (समान `cmd1/cmd2`, बढ़ता हुआ `chunkIndex`) और क्रम में पुनः जोड़ा जाता है।

### Opcode परंपरा (✅ वायर पर पुष्टि की गई)

- `cmd1 = 0xFFFF`: `cmd2` में `0x80xx`/`0x90xx` = फोन→घड़ी (अनुरोध/सेट); `0x00xx`/`0xa0xx` = घड़ी→फोन (उत्तर)। जोड़े निम्न बाइट द्वारा मेल खाते हैं (`0x9055`↔`0xa055`, `0x8051`↔`0x0051`)।
- फीचर-विशिष्ट `cmd1`: `cmd2` प्रत्यय = `0x0001` **SET**, `0x0002` **GET**, `0x0003` **ACK**।

### चंक बॉडी

प्रत्येक चंक के लिए, बॉडी है `payloadPiece ‖ CRC32_LE(payloadPiece)` (4-बाइट CRC, लिटिल-एंडियन, zlib/IEEE)। यदि कमांड **एन्क्रिप्टेड** है (§3 देखें), तो पूरा `payloadPiece ‖ CRC` फिर AES-128-CBC/PKCS7 से एन्क्रिप्ट किया जाता है और वह सिफरटेक्स्ट फ्रेम का `chunk` बन जाता है।

**प्लेनटेक्स्ट विचित्रता:** प्लेनटेक्स्ट opcodes के लिए घड़ी `chunkLen` में 4-बाइट CRC को *गिनती* है लेकिन उसे **प्रसारित** नहीं करती। इसलिए प्लेनटेक्स्ट फ्रेम को डिकोड करते समय, वास्तविक डेटा लंबाई `chunkLen − 4` होती है। (एन्क्रिप्टेड फ्रेम्स CRC को सिफरटेक्स्ट के अंदर सामान्य रूप से ले जाते हैं।)

चंक आकार (ताकि एन्क्रिप्टेड चंक्स AES ब्लॉक सीमाओं पर पहुँचें), `maxWrite = mtu − 3` के साथ:
- एन्क्रिप्टेड: `floor((maxWrite − 11) / 16) * 16 − 4 − 1`
- प्लेनटेक्स्ट: `maxWrite − 11 − 4 − 1`

✅ सभी देखे गए एन्क्रिप्टेड फ्रेम `chunkLen` मान 16 के गुणक थे (ब्लॉक संरेखण बना रहता है)।

---

## 3. क्रिप्टोग्राफिक प्रिमिटिव्स

- **AES-128-CBC** जिसमें **PKCS7** पैडिंग और एक **निश्चित IV** (फर्मवेयर `CmfCharacteristic.AES_IV` से):
  `50 51 52 53 54 55 56 57 60 61 62 63 64 65 66 5A`।
- **CRC32** (zlib/IEEE), 4 लिटिल-एंडियन बाइट्स के रूप में उत्सर्जित।
- **SHA-256** भागों के संयोजन पर।

कुंजी व्युत्पत्ति:```
authkey      = SHA256( rnd1 ‖ rnd2 ‖ secret )[0..16]      // persisted across sessions
sessionKey   = SHA256( nonce ‖ authkey )[0..16]           // per connection
  • secret = 16-बाइट डिवाइस सीक्रेट (वॉच से शेल कमांड AT GETSECRET → GETSECRET:<32-hex>,OK के माध्यम से प्राप्त किया जा सकता है)।
  • rnd1 = फोन द्वारा चुने गए 16 रैंडम बाइट्स; rnd2 = वॉच से 16 रैंडम बाइट्स।
  • nonce = वॉच के nonce रिप्लाई से बाइट्स।

कुंजी सेट होने के बाद, सभी कमांड-चैनल फ्रेम AES-एन्क्रिप्टेड होते हैं सिवाय §5 में सूचीबद्ध प्लेनटेक्स्ट ऑपकोड्स के।

✅ दोनों डेरिवेशन मान्य किए गए: रूट किए गए फोन के ntwatch.db से प्राप्त authkey कैप्चर किए गए rnd1/rnd2/secret से व्युत्पन्न मान से मेल खाता था; कैप्चर किए गए nonce से पुनरुत्पादित sessionKey लाइव फ्रेम को डिक्रिप्ट करता है।


4. प्रमाणीकरण / पेयरिंग हैंडशेक

दो एंट्री पथ एक ही nonce/confirm टेल साझा करते हैं।

4.1 पहली बार पेयरिंग (डिवाइस सीक्रेट होने पर)```

phone → (shell) AT GETSECRET watch → (shell) GETSECRET:<32hex>,OK phone: rnd1 = random16 ; signed1 = SHA256(rnd1 ‖ secret) phone → AUTH_PAIR_REQUEST (plaintext) payload = rnd1(16) ‖ signed1(32) // 48 B watch → AUTH_PAIR_REPLY (plaintext) payload = rnd2(16) ‖ signed2(32) // 48 B phone verifies signed2 == SHA256(rnd2 ‖ secret) phone: authkey = SHA256(rnd1 ‖ rnd2 ‖ secret)[0..16] → set crypto key = authkey phone → AUTH_PHONE_NAME (encrypted) payload = 0xA5 ‖ model(UTF-8) // e.g. "CMF Watch Pro 2" watch → AUTH_WATCH_MAC (encrypted) phone → AUTH_NONCE_REQUEST (encrypted) payload = 0xA5 watch → AUTH_NONCE_REPLY (encrypted) payload = nonce phone: sessionKey = SHA256(nonce ‖ authkey)[0..16] → set crypto key = sessionKey phone → AUTHENTICATED_CONFIRM_REQUEST (encrypted) payload = 0xA5 watch → AUTHENTICATED_CONFIRM_REPLY (encrypted) → state = Initialized

root@kitploit:~
`AUTH_FAILED (0xFFFF,0xA061)` या हस्ताक्षर बेमेल होने पर, प्रमाणीकरण विफल हो जाता है।

### 4.2 पुनः कनेक्ट (authkey पहले से ज्ञात)```
        set crypto key = authkey (persisted)
phone → AUTH_PHONE_NAME      (encrypted)  payload = 0xA5 ‖ model
watch → AUTH_WATCH_MAC       (encrypted)
phone → AUTH_NONCE_REQUEST   (encrypted)  payload = 0xA5
watch → AUTH_NONCE_REPLY     (encrypted)  payload = nonce
        sessionKey = SHA256(nonce ‖ authkey)[0..16]   → set crypto key = sessionKey
phone → AUTHENTICATED_CONFIRM_REQUEST (encrypted)  payload = 0xA5
watch → AUTHENTICATED_CONFIRM_REPLY   (encrypted)  → Initialized

✅ पुनः कनेक्ट क्रम (बिना शेल ट्रैफ़िक) वास्तविक कैप्चर पर बरकरार देखा गया।

4.3 पोस्ट-ऑथ इनिशियलाइज़ेशन (चरण 2)

⚠️→✅ डेटा क्वेरी से पहले TIME अनिवार्य है। Initialized के बाद, वॉच BATTERY, SERIAL_NUMBER_GET, या ACTIVITY_FETCH_* हैंडशेक का उत्तर नहीं देगी जब तक कि सत्र में TIME (FFFF 8004) न भेजा जाए — इसके बिना, केवल एक अनचाहा FIRMWARE_VERSION_RET आता है और बाकी सब टाइमआउट हो जाता है। ✅ लाइव पुष्टि (Pixel 8a): बिना TIME के तीनों GET भेजने पर → केवल फ़र्मवेयर उत्तर देता है; पहले TIME भेजने पर → बैटरी और सीरियल उत्तर देना शुरू करते हैं।

अनुशंसित चरण-2 क्रम: TIME → FIRMWARE_VERSION_GET → SERIAL_NUMBER_GET → BATTERY (0xA5) → कॉन्फ़िग पुश → हेल्थ सिंक (§8)।

4.4 GET → SET इको पैटर्न (✅)

अधिकांश सेटिंग्स के लिए कोई अलग "रीड" ऑपकोड नहीं है। *_GET (cmd2 = 0x0002, पेलोड 0xA5) भेजने पर वॉच SET ऑपकोड के साथ उत्तर देती है (cmd2 = 0x0001) जिसमें वर्तमान मान होता है। SET कमांड को cmd2 = 0x0003 और खाली बॉडी के साथ स्वीकार किया जाता है।


5. प्लेनटेक्स्ट बनाम एन्क्रिप्टेड

कुंजी सेट होने के बाद फ़्रेम AES-एन्क्रिप्टेड होते हैं, इन ऑपकोडों को छोड़कर, जो हमेशा प्लेनटेक्स्ट होते हैं:

  • AUTH_PAIR_REQUEST (FFFF 8047), AUTH_PAIR_REPLY (FFFF 0048)
  • DATA_CHUNK_WRITE_WATCHFACE (FFFF 9064), DATA_CHUNK_WRITE_FIRMWARE (FFFF 9042), DATA_CHUNK_WRITE_AGPS (FFFF 905F)

फ़्रेम हेडर (cmd1/cmd2) हमेशा स्पष्ट रूप में यात्रा करते हैं, इसलिए कमांड अनुक्रम किसी भी कैप्चर में बिना कुंजी के भी दिखाई देता है — केवल एन्क्रिप्टेड पेलोड को sessionKey की आवश्यकता होती है।


6. ऑपकोड संदर्भ (cmd1, cmd2)

GET/SET/REQUEST = फ़ोन→वॉच; RET/REPLY/ACK/RESPONSE/DATA = वॉच→फ़ोन।

सत्र / डिवाइस

प्रमाणीकरण

सूचनाएँ / कॉल / खोज

संगीत

नामcmd1,cmd2
MUSIC_INFO_SET / _ACKFFFF 905C / FFFF A05C
MUSIC_BUTTONFFFF A05D

अलार्म / संपर्क / रिमाइंडर

कॉन्फ़िग

मौसम

नामcmd1,cmd2
WEATHER_SET_1 (वह जो काम करता है)FFFF 906B
WEATHER_SET_2 (Pro 2 पर अनदेखा — §9 देखें)0066 0001

वॉच फेस / डायल

स्वास्थ्य / सिंक

केवल-JS ऑपकोड (FFFF 8051, FFFF 0051, FFFF 90A2, FFFF 90C5, FFFF A056, FFFF 908A/908B ChatGPT स्थिति/समर्थन) ऐप के Hermes बाइटकोड में संभाले जाते हैं, Java परत में नहीं। उनके हेडर कैप्चर में दिखाई देते हैं लेकिन पेलोड शब्दार्थ ⚠️ [अनिश्चित] हैं।

बल्क डेटा ट्रांसफर (डेटा चैनल)

वॉचफेस / फ़र्मवेयर / AGPS एक init → चंक-अनुरोध/चंक-लिखें लूप → फिनिश-ack का उपयोग करते हैं:

(सभी cmd1 = FFFF।) वॉच DATA_CHUNK_REQUEST_*(offset, length) उत्सर्जित करके लूप चलाती है (offset/length = u32 बिग-एंडियन); फ़ोन डेटा विशेषता पर payload[offset..offset+length] लेकर DATA_CHUNK_WRITE_* के साथ उत्तर देता है। विवरण के लिए §11–§12 देखें।


7. समय और टाइमज़ोन

TIME (FFFF 8004) पेलोड = epochSeconds(i32, BE) ‖ utcOffsetMillis(i32, BE)। प्रमाणीकरण के तुरंत बाद भेजा जाता है ताकि वॉच स्थानीय समय दिखाए (और डेटा क्वेरी अनब्लॉक करे — §4.3 देखें)।

⚠️ वॉच से स्वास्थ्य टाइमस्टैम्प UTC हैं। साथी ऐप को स्थानीय कैलेंडर दिन / दिन-का-समय प्राप्त करने से पहले स्थानीय UTC ऑफ़सेट जोड़ना चाहिए। (कच्चे UTC दिन द्वारा स्वास्थ्य को बकेट करने से दिन गलत स्थानीय समय पर बदल जाता है।)

TIME_FORMAT (005F 0001) पेलोड = 1 बाइट: 00 = 24 घंटे, 01 = 12 घंटे।


8. स्वास्थ्य सिंक

  1. फ़ोन ACTIVITY_FETCH_1 भेजता है; वॉच ACTIVITY_FETCH_ACK_1 के साथ उत्तर देती है (पहला बाइट 01 ⇒ तैयार)।
  2. फ़ोन ACTIVITY_FETCH_2 भेजता है; फिर वॉच डेटा फ़्रेमों का एक बर्स्ट धकेलती है: ACTIVITY_DATA, HEART_RATE_*, SPO2, STRESS, SLEEP_DATA, WORKOUT_SUMMARY[_V3]।
  3. प्रत्येक को प्रति-मिनट नमूनों / सत्रों में पार्स किया जाता है और स्थानीय दिन द्वारा एकत्रित किया जाता है।

सिंक अनुक्रमिक है (TIME का पालन करना चाहिए; वॉच ACK_2 के बाद स्ट्रीम जारी करती है), एकल बर्स्ट नहीं। एक भारी सत्र ~160 सेकंड में ~170–210 सूचना फ़्रेम धकेलता है। ✅

8.1 गतिविधि रिकॉर्ड — ACTIVITY_DATA (प्रत्येक 32 बाइट, LE) ✅

कैलोरी इकाई: गतिविधि कैलोरी cal (ग्राम-कैलोरी) में रिपोर्ट की जाती हैं। दैनिक योग को 1000 से विभाजित करके kcal प्राप्त करें। (वर्कआउट-सारांश कैलोरी, इसके विपरीत, पहले से ही kcal में हैं।)

8.2 HR / SpO₂ / तनाव नमूने ✅

  • मैनुअल/ऑटो HR, वर्कआउट HR, SpO₂, तनाव = 8 बाइट प्रत्येक: timestamp(i32 LE) ‖ value(i32 LE) (value = bpm / SpO₂ % / तनाव सूचकांक)।
  • आराम HR (00DA 0001) अलग है — 5 बाइट: timestamp(i32 LE) ‖ hr(u8)। ✅ लाइव उदाहरण 5e dc 29 6a 4e → ts, hr = 78 bpm। तनाव स्कोर श्रेणियाँ: 1–29 / 30–59 / 60–79 / 80–99।

8.3 नींद — SLEEP_DATA (18-बाइट हेडर + N × 8-बाइट रिकॉर्ड) ✅

एक SLEEP_DATA = एक नींद सत्र; एक रात में कई हो सकते हैं (माइक्रो-जागरण सत्र विभाजित करते हैं)।

हेडर:

प्रत्येक 8-बाइट रिकॉर्ड: timestamp(u32) ‖ duration_s(u16) ‖ stage(u16)। स्टेज कोड: 1 = Deep, 2 = Core/light, 3 = REM, 4 = Awake। ✅ पूरी रात के विरुद्ध मान्य (दो सत्र, D/C/R/A योग मेल खाते हैं)।

8.4 वर्कआउट सारांश — WORKOUT_SUMMARY v1 (54 बाइट) / _V3 (0160 0001)

v1: start(u32), end(u32), duration_s(u32), फिर type/calories/steps/distance/avg-HR और एक GPS/विस्तारित ब्लॉक। ✅ v1 लेआउट फ़र्मवेयर के विरुद्ध पुष्टि की गई। WORKOUT_SUMMARY_V3 उसी डेटा के लिए एक नया लेआउट है साथ ही एक ~40-बाइट विस्तारित ब्लॉक (exerciseLoad, aerobic/anaerobic, recoveryTime, VO₂max, cadence, PAI, सर्वश्रेष्ठ-रन समय…)। फ़ील्ड सेट ज्ञात है (ऐप के Room DB से) लेकिन उस 40-बाइट ब्लॉक के भीतर सटीक बाइट ऑफ़सेट ⚠️ [अनिश्चित] हैं — उन्हें बंद करने के लिए GPS वर्कआउट के एक कच्चे कैप्चर की आवश्यकता है।


9. चयनित कमांड पेलोड

स्ट्रिंग्स UTF-8 हैं, फ़ील्ड आकार के लिए बाइट-ट्रंकेटेड (ट्रंकेशन एक मल्टी-बाइट वर्ण को विभाजित कर सकता है, फ़र्मवेयर के s.encode()[:max] व्यवहार से मेल खाता है); छोटे फ़ील्ड दाईं ओर शून्य-पैडेड हैं।

  • APP_NOTIFICATION (0065 0001) ✅: iconCode(1) ‖ 0x00 ‖ when(u32 BE) ‖ titleLen(1) ‖ title ‖ body। iconCode ऐप आइकन चुनता है (WhatsApp=8, Telegram=12, Instagram=18, Gmail=27; अज्ञात=0xFF)। शीर्षक ≤ 20 बाइट, बॉडी ≤ 128 बाइट। क्लाइंट से भेजा गया → वॉच ने प्रदर्शित किया + ACK 0065 0003।
  • BATTERY (005C 0001) ✅: उत्तर = level(1) ‖ charging(1) (जैसे 3b 00 = 59 %, चार्ज नहीं हो रहा)।
  • SERIAL_NUMBER_RET (00DE 0001) ✅: len(1) ‖ ASCII (जैसे 10 + "CI04102520008192")।
  • USER_INFO (0095 0001) ✅: height_cm(1) ‖ weight_kg(1) ‖ age(1) ‖ gender(1: 1=M) (जैसे = 172 सेमी / 73 किग्रा / 31 / पुरुष)।

10. कार्यान्वयन नोट और विचित्रताएँ

  • कोडेक्स में कोई सिस्टम क्लॉक नहीं: एन्कोडर now/utc_offset को स्पष्ट पैरामीटर के रूप में लेते हैं (नियतात्मक, परीक्षण योग्य)। परिवहन वास्तविक समय प्रदान करता है।
  • TIME सब कुछ गेट करता है (§4.3) — इसे पहले भेजें या वॉच डेटा क्वेरी पर मौन रहती है।
  • प्लेनटेक्स्ट CRC गिनती (§2) गलत करना आसान है — प्लेनटेक्स्ट फ़्रेम CRC का विज्ञापन करते हैं लेकिन इसे छोड़ देते हैं।
  • एंडियननेस: हेडर + ऑपकोड BE; पेलोड पूर्णांक LE; अपवाद — GOALS_SET और GPS_PUSH बिग-एंडियन हैं, और बल्क-ट्रांसफर ऑफ़सेट/लंबाई बिग-एंडियन हैं।
  • MTU: चंक आकार की गणना की जाती है ताकि एन्क्रिप्टेड चंक 16-बाइट AES ब्लॉकों के साथ संरेखित हों।
  • authkey स्थायी है (पहली पेयरिंग के बाद इसे संग्रहीत करें); sessionKey प्रति-कनेक्शन है और हर पुनः कनेक्ट पर वॉच नॉन्स से प्राप्त होता है।

11. वॉच फेस / डायल — लेखन

वॉच समर्थन करती है (a) फोटो/कस्टम डायल (एक पृष्ठभूमि छवि + एक फ़र्मवेयर-चित्रित डिजिटल घड़ी) और (b) संरचित डायल (अंतर्निहित / स्टोर फेस: एक पृष्ठभूमि प्लस स्थित स्प्राइट परतें, हाथ, और टेक्स्ट विजेट)। दोनों §6 में init → चंक लूप के माध्यम से डेटा चैनल पर स्थानांतरित होते हैं।

वास्तव में क्या काम करता है (✅ लाइव मान्य): किसी भी छवि से फोटो डायल बनाना और उसे स्थापित करना; 103 स्टोर डायल में से किसी को ऑफ़लाइन स्थापित करना; एक संरचित डायल को रीस्किन करना (पृष्ठभूमि या किसी गैर-पृष्ठभूमि स्प्राइट को स्वैप करना) और उसकी परतों को स्थानांतरित करना; सक्रिय फेस को पुनःक्रमित / स्विच करना; और शुरू से एक संरचित डायल बनाना — 0x20 दृश्य लिफाफा``` INIT1 8052 (payload A5) → 0052 [0]=01 INIT2 9063 (photo, APPEND) | 9075 (structured, REPLACE) → A063 / A075 [0]=01 [ watch → DATA_CHUNK_REQUEST A064 (offset, length; u32 BE, +progress u8) phone → DATA_CHUNK_WRITE 9064 (bytes[offset..offset+length], plaintext) ] × N FINISH A065 → 9065 (payload A5)

root@kitploit:~
Finish reply byte: `01` = सक्रिय और सहेजा गया; `0a` = संग्रहीत लेकिन **सक्रिय नहीं** / अस्वीकृत। 
Android पर प्रत्येक `DATA_CHUNK_WRITE` को **प्रति फ्रेम एक BLE राइट** के रूप में भेजा जाना चाहिए — 
कॉनकैटेनेट करना और MTU द्वारा फिर से स्लाइस करना हेडर को डीसिंक कर देता है और घड़ी ऑफसेट 0 के लिए लूप करती रहती है।

- **`9063` (फोटो) = APPEND।** डायल सूची बढ़ती है (6→7); `watchfaceId = 0xFFFFFFFF` (कस्टम 
  सेंटिनल) ताकि इसे डुप्लिकेट के रूप में कभी अस्वीकार न किया जाए, और घड़ी इसे स्वतः सक्रिय कर देती है।
- **`9075` (स्ट्रक्चर्ड) = REPLACE** `old_id` स्लॉट। `old_id` **पहले से ही** सूची में होना **चाहिए** 
  (अन्यथा `0a`)। पहले से मौजूद किसी id को फिर से इंस्टॉल करने के लिए, **पहले उसे डिलीट करें** (9055 लिस्ट-माइनस-id) 
  फिर "फ्रेश" अपलोड करें — किसी id को उसी स्थान पर पुनः उपयोग करने पर `0a` मिलता है।

### 11.3 फोटो / कस्टम डायल — ✅ एंड-टू-एंड पूर्ण रूप से मान्य

**कंटेनर** (बाइट-सत्यापित राउंड-ट्रिप; सभी फ़ील्ड लिटिल-एंडियन):```
0x00  magic     6c 8d c4 a5
0x04  count     12 00 00 00   (=18)  [constant, NOT an element count]
0x08  00 × 8
0x10  lenFull   u32 LE        (length of the whole FULL block: tag+len+payload)
0x14  FULL  tag 04 48 47 3a ‖ payloadLen(u32 LE) ‖ LZ4(RGB565-LE)   → 466×466  [raw 434312 B]
      THUMB tag 04 38 c4 21 ‖ payloadLen(u32 LE) ‖ LZ4(RGB565-LE)   → 270×270  [raw 145800 B]
EOF-4 magic     6c 8d c4 a5   [trailer = magic repeated]

कोडेक = मानक LZ4 ब्लॉक, RGB565 लिटिल-एंडियन, टॉप-डाउन (payloadLen पहले LZ4 बाइट से गिना जाता है)। आधिकारिक ऐप LZ4-HC का उपयोग करता है और 21-बाइट LZ4-ब्लॉक हेडर/फुटर हटा देता है; एक सादा केवल-लिटरल्स LZ4 एन्कोडर भी काम करता है — घड़ी किसी भी मान्य LZ4 को स्वीकार करती है, बाइट-पहचान आवश्यक नहीं है। अंकित वृत्त (केंद्र 233,233, त्रिज्या 233) के बाहर के पिक्सेल 0x0000 पर सेट होते हैं।

9063 के लिए INIT_2 — सटीक हेडर (✅ यह वही है जो काम करता है):``` 01 ‖ size(u32 BE) ‖ FF FF FF FF ‖ 01 01 01 ‖ styleId(u16 BE) ‖ posX(u16 BE) ‖ posY(u16 BE) ‖ color565(u16 BE) ‖ FF × 8

root@kitploit:~
`size` = सटीक `.bin` लंबाई; `FFFFFFFF` = कस्टम `watchfaceId`; `styleId` 0–4 बिल्ट-इन
डिजिटल-क्लॉक लेआउट चुनता है (यह हमेशा खींचा जाता है — कोई "बंद" विकल्प नहीं है); `posX/posY` इसे स्थित करते हैं (ज्ञात-अच्छा
56 / 77); `color565` इसे रंग देता है (जैसे `FFFF` = सफेद)। ⚠️ छोटा `A5 ‖ size ‖ watchfaceId` रूप
finish `0a` के साथ **अस्वीकृत** है — ऊपर दिया गया पूरा हेडर उपयोग करें। (संदर्भ कार्यान्वयन:
`core-rust/engine.rs::build_wf_init2`, आधिकारिक ऐप में `C6135t.m31104u` को दर्शाता है।)

**विधि:** छवि को 466×466 (और एक 270×270 थंबनेल) में आकार बदलें, RGB565-LE टॉप-डाउन में बदलें,
वैकल्पिक रूप से सर्कल के बाहर के पिक्सेल शून्य करें, प्रत्येक को LZ4-संपीड़ित करें, ऊपर दिया गया कंटेनर इकट्ठा करें, और
`watchfaceId = 0xFFFFFFFF` के साथ `9063` पाइपलाइन के माध्यम से अपलोड करें। (संदर्भ कोडेक: `core-rust/watchface.rs`,
`work/codec_dfa.py`।)

### 11.4 संरचित / स्टोर डायल — कंटेनर और कोडेक ✅

**फ़ाइल लेआउट** — 36-बाइट हेडर को EOF पर **बाइट-समान रूप से 36-बाइट फुटर के रूप में दोहराया जाता है**
(✅ 15 डायल पर सत्यापित; एक पार्सर को ऐसी फ़ाइल को अस्वीकार करना चाहिए जहाँ वे भिन्न हों):```
[36-byte header][scene TLV (§11.7)][asset pool][36-byte header again]

हेडर (सभी 103 स्टोर डायल में समान संरचना; सभी फ़ील्ड लिटिल-एंडियन हैं):``` 0x00 crc_tree u32 LE [CRC32-raw of header[0x04:0x24] ‖ scene section] ✅ see below 0x04 magic 01 00 00 XX [XX = 0x00 or 0x02; both seen, meaning of 0x02 unknown] 0x08 name char[16] [NUL-terminated, e.g. "SlopeTime", "Metaball"; may carry a non-zero tail after the NUL (@0x17) — round-trip it verbatim] 0x18 size_a u32 LE [= filesize − 36 = footer offset = header+body] ✅ 103 dials 0x1c size_b u32 LE [asset-pool length, exactly] ✅ 15 dials 0x20 crc_assets u32 LE [CRC32-raw of the asset pool] ✅ see below 0x24 … [body starts here: the 0x20 scene container, §11.7]

root@kitploit:~
> ⚠️ **सुधार (पिछले "कोई ब्लॉकिंग चेकसम नहीं है" को प्रतिस्थापित करता है)।** पहले के संशोधनों में `@0x00` को एक
> प्रति-डायल id/हैश के रूप में और `@0x20` को "3× u32 id/हैश शब्द \[CRC नहीं]" के रूप में पढ़ा गया था, और कहा गया था कि CRC32/Adler32/
> बाइट-सम सभी मेल खाने में विफल रहते हैं। दोनों शब्द **CRC32 हैं** — पहले के परीक्षण उन्हें चूक गए क्योंकि
> वैरिएंट गैर-मानक है, और क्योंकि "`0x20` पर 3 शब्द" की व्याख्या एकल CRC
> शब्द को `0x24` पर शुरू होने वाले सीन कंटेनर के पहले बाइट्स के साथ मिला रही थी (इसी तरह "`0x2c` पर दोहराया गया नाम"
> सीन का `0x86` नाम नोड है, §11.11)। खोज
> [freethinkel/fmc](https://github.com/freethinkel/fmc) से; यहाँ पुनः सत्यापित।

**CRC32-raw** = परावर्तित IEEE बहुपद `0xEDB88320`, **`init = 0`**, और **कोई अंतिम XOR नहीं** — अर्थात
न तो मानक `crc32` का `init=0xFFFFFFFF` और न ही `^0xFFFFFFFF`। यही पूरा कारण है कि
तैयार-मिला CRC32 कभी मेल नहीं खाता। क्रम निर्भरता पर ध्यान दें: `crc_assets` उस सीमा के अंदर स्थित है जो
`crc_tree` द्वारा कवर की जाती है, इसलिए **पहले `@0x20` लिखें, फिर `@0x00` की गणना करें**।```python
def crc32_raw(data: bytes) -> int:          # tab = standard 0xEDB88320 reflected table
    c = 0                                    # init 0, no final inversion
    for b in data: c = tab[(c ^ b) & 0xFF] ^ (c >> 8)
    return c & 0xFFFFFFFF

crc_tree   = crc32_raw(f[0x04:0x24] + f[0x24:first_asset])
crc_assets = crc32_raw(f[first_asset:len(f)-36])

सत्यापित: 9/9 शुद्ध स्टोर डायल दोनों शब्दों पर मेल खाते हैं, और इस रिपॉजिटरी के अपने 6/6 टेम्पलेट crc_tree पर मेल खाते हैं।

🟡 फर्मवेयर किसी भी CRC को लागू करता हुआ प्रतीत नहीं होता। इस रिपॉजिटरी ने 9075 के ऊपर जो भी डायल इंस्टॉल किए हैं — जिसमें समान-फुटप्रिंट इन-प्लेस एडिट पथ (§11.6) द्वारा निर्मित रीस्किन भी शामिल हैं, जो एसेट पेलोड और X/Y बाइट्स को हेडर की पुनर्गणना किए बिना बदलता है — डिवाइस पर ठीक से रेंडर हुए। इसलिए पुराना CRC वह चीज़ नहीं है जो 0a अस्वीकृति का कारण बनती है (वह कंटेनर-विंडो इनवेरिएंट है, §11.7)। CRCs को वैसे भी सही-लिखें मानें: सस्ते, और फॉर्मेट में एकमात्र ज्ञात इंटीग्रिटी फ़ील्ड। जो भी सीन या एसेट पूल को फिर से लिखता है, उसे दोनों शब्दों की पुनर्गणना करनी चाहिए।

स्टब डायल (~173 B, जैसे ids 273/274/277) ROM में बेक किए गए फेस के लिए प्लेसहोल्डर हैं: हेडर + डायरेक्टरी, कोई वास्तविक एसेट नहीं।

एसेट्स — प्रत्येक dimsWord(u32 LE) ‖ len(u32 LE) ‖ LZ4(payload) है, जहाँ cf = dimsWord & 0x1f, w = (dimsWord >> 10) & 0x7FF, h = (dimsWord >> 21) & 0x7FF, और len पहले LZ4 बाइट से गिना जाता है (वह 1f 00 01 00 जो आप अक्सर वहाँ देखते हैं, पहला LZ4 टोकन है — इसे छोड़ें नहीं)। डीकंप्रेस्ड आकार = w·h·bpp:

✅ 103 डायल्स में सभी 4151/4151 एसेट्स w·h·bpp पर एक मानक lz4.block डीकंप्रेसर के साथ बिल्कुल डिकोड होते हैं। पारदर्शिता अल्फा बाइट (cf=5/24) या 0x0000 (वृत्त के बाहर cf=4) है — कोई RLE नहीं है और कोई "एस्केप" नहीं है। एन्कोड = पुनः-रास्टर → मानक LZ4 → [dimsWord][len][LZ4]।

9075 के लिए INIT_2 — AES-एन्क्रिप्टेड बॉडी:``` kind(1) ‖ old_id(u32 LE) ‖ new_id(u32 LE) ‖ file_len(u32 LE)

root@kitploit:~
`kind` = `0x02`/`0x03`; `old_id` = वर्तमान सक्रिय डायल (`9055` से); `file_len` = वास्तविक `.bin`
आकार (= `@0x18 + 36`). स्टोर `.bin` को जैसा-का-तैसा इंस्टॉल करना गारंटीड रास्ता है (Ring Data id 359 +
102 अन्य पुष्टि किए गए). (संदर्भ: `core-rust/engine.rs::build_dial_replace_init`.)

### 11.5 संरचित निर्देशिका व्याकरण ✅ (डिकोड किया गया और लागू किया गया — संशोधित 2026-07-02)

> **⚠️ संशोधन (2026-07-02): नीचे दिया गया फ्लैट `61 01 00` रिकॉर्ड स्कीमा व्यवस्थित रूप से
> OFF-BY-ONE था।** सीन बॉडी एक साफ TLV है (§11.7); एक drawable **लीफ बॉडी** (टैग `0x30`/`0x38`
> स्टैटिक, `0x70` पॉइंटर) है:
>
> ```
> 01 xx 00 [X u16][Y u16] …attrs… 61 [count u16][base u32][count×id u16] [05 05 00 01 pivX pivY]
> ```
>
> - attr `0x01` बॉडी खोलता है: **X,Y = 466² कैनवास पर टॉप-लेफ्ट** (SDK के `sty_picture_t` का `s16 x,y`).
> - **फ्रेम टेबल `61 …` बॉडी को बंद करती है** (`base` = एसेट ptr; `count` 1 = इमेज, 10/11 =
> डिजिट एटलस — पुराना "रिकॉर्ड टाइप `0a/0b`" वास्तव में यही count था! — 7/13/2 = कॉम्प्लिकेशन
> फ्रेम शीट).
> - पॉइंटर एक्स्ट्रा: `0x01` attr के अंदर सोर्स+स्केल `[src] 00 3c 00`; पिवट `05 05 00 01 [pivX][pivY]`
> **ट्रेलर** में। **रोटेशन सेंटर = प्रति पॉइंटर `(X+pivX, Y+pivY)`** —
> कोई फिक्स्ड (233,233) नहीं: ऑफ-सेंटर सबडायल मौजूद हैं (जैसे डायल 366 की सुइयां 150,150 के आसपास घूमती हैं).
>
> `61 01 00` के लिए लीनियर स्कैन तत्व **N** के फ्रेम-टेबल+पिवट को तत्व **N+1** के X/Y
> (और टैग बाइट, पुराना "f3") से जोड़ रहा था — यह केवल एनालॉग डायल पर *सही दिखता* था जिनकी
> आसन्न सुइयां लगभग समान ज्यामिति साझा करती हैं। "कॉम्पैक्ट वेरिएंट दीवार" (spec 24 §24.4.5)
> यही गलत पठन था। `core-rust/watchface_struct.rs` में `scan_scene_drawables` के रूप में लागू किया गया
> और `wfweb/src/codec/parse.ts` (सीन = इमेज/पॉइंटर्स के लिए प्राथमिक स्रोत; फ्लैट स्कैन टेक्स्ट +
> नॉन-एनवेलप फॉलबैक के लिए रखा गया). `wfweb/compare.html` ओरेकल द्वारा मान्य (रेंडर बनाम आधिकारिक
> स्टोर PNG, 99 डायल): 64→72 अच्छे, 8→5 खराब, माध्य अंतर 9.3→7.4%.

ऐतिहासिक फ्लैट-रिकॉर्ड पठन (प्रतिस्थापित, संदर्भ के लिए रखा गया):

- **स्टैटिक इमेज** (`61 01 00`): `asset_ptr(u32) ‖ elemId(u16) ‖ 05 05 00 01 ‖ pivotX(u16) ‖
  pivotY(u16) ‖ 3B ‖ 01 ‖ 1b 00 ‖ X(u16) ‖ Y(u16)`. 466² कैनवास पर टॉप-लेफ्ट = `(X−pivotX, Y−pivotY)`.
- **पॉइंटर/सुई** — वही इमेज रिकॉर्ड, रनटाइम पर घुमाया गया। **रोटेशन सेंटर = `(X+pivotX, Y+pivotY)`**
  (एनालॉग डायल पर ≈ 233,233). **डेटा स्रोत रिकॉर्ड ऑफसेट `+36` पर एक `u8` है**, स्केल `u16` at
  `+38` (=60): `0x0a`/`0x70` = घंटा (`h·30°+m·0.5°`), `0x0e`/`0x71` = मिनट (`m·6°+s·0.1°`),
  `0x12`/`0x72` = सेकंड (`s·6°`). ✅ गेटर्स को डिसअसेंबल करके पुष्टि की गई (RTC फॉलबैक 10:10:30).
- **टेक्स्ट / नंबर विजेट** (`61 0a 00`): `asset_ptr(u32) ‖ [10×u16 फॉन्ट मेट्रिक्स] ‖ 40 01 00 ‖ फ्लैग ‖
  3B ‖ 01 ‖ u16 ‖ X(u16) ‖ Y(u16)`. `asset_ptr` ग्लिफ "0" की ओर इशारा करता है; अंक *d* = एसेट at
  `index("0") + d` (10 क्रमागत cf=5 स्प्राइट, जैसे `0123456789` और `,°` विराम चिह्न). ✅ रेंडर किया गया.
- **कॉम्प्लिकेशन फिल = फ्रेम-इंडेक्स** (✅ डिजिट/एनम/गेज कॉम्प्लिकेशन के लिए पुष्टि, count>1): मान
  `.bin` में एक **प्री-रेंडर फ्रेम शीट** को इंडेक्स करता है — `frame = (count−1)·val/100` (प्रतिशत) या
  `frame = value` (फ्लिप डिजिट / एनम). फ्रेम टेबल = सब-रिकॉर्ड `61 ‖ count(u16) ‖ base(u32) ‖
  count×id(u16)`. जैसे 327 Digit Max का बड़ा घंटा 13-फ्रेम शीट है (नंबर 0–12), `frame = hour`.
- **प्रोग्रेस रिंग / आर्क = रनटाइम सेक्टर क्लिप** (✅ 2026-07-02, **spec 25 §2 में "रिंग फ्रेम शीट हैं"
  पठन को सही करता है**): तत्व टैग **`0x81`** एक **एकल** फुल डिस्क रखता है (`61` फ्रेम-टेबल
  `count == 1`), और आंशिक वेज वह डिस्क है जो **पाई सेक्टर में क्लिप की गई है** (`frac = value/max`,
  12 बजे से घड़ी की दिशा में) — 322 Glare 2 पर पिक्सेल-दर-पिक्सेल सत्यापित और **20 डायल** पर `count==1`
  पुष्टि की गई. डिस्क पर: `0x81` बॉडी = सब `0x01` (ज्यामिति `x@+0 y@+2 w@+4 h@+6`, इनलाइन `61 1 base`
  = डिस्क) + सब `0x5b` (आर्क स्पेक). wfweb में लागू (`blendSector`).
  ⚠️ `0x5b` सब-रिकॉर्ड **केवल "`max` u16 `@+4`" नहीं है** जैसा पहले दस्तावेज़ित किया गया था — वह
  `max i32` के निचले आधे हिस्से को पढ़ता था और **स्टार्ट/एंड स्वीप कोण और स्ट्रोक चौड़ाई** को चूक गया जो
  उसके ठीक बाद बैठते हैं। एक प्रोसीजरल सिबलिंग `0x80`/`0x5a` भी है (स्पष्ट त्रिज्या के साथ) जिसे यह
  दस्तावेज़ कभी कवर नहीं करता था। पूर्ण रिकॉर्ड लेआउट, और पिछली "12 बजे से घड़ी की दिशा में"
  धारणा क्या गलत थी: **§11.15**.
- **डेटा स्रोत id** — तत्व का `82` attr-ब्लॉक `delim+3` पर बैठता है (अंतिम `40 01 00` के बाद),
  और **स्रोत id `+0x14` पर एक `u8` है** (साथ ही `relX@+0x07 s16`, `relY@+0x09 s16`, `anchor@+0x0C/0E`,
  `mode@+0x15`, `frame-count@+0x1A`). Anchor < 0 = माता-पिता के किनारे पर संरेखित करें. 🔎 फर्मवेयर
  id को `0x101f371c` पर 142-एंट्री गेटर टेबल के माध्यम से हल करता है (प्रत्येक `ux2sys_get(type)` कॉल करता है).
  ⚠️ **id को संरचना के `meta[9]` के रूप में पढ़ना पसंद करें (§11.11)** — एक फिक्स्ड फील्ड — इस फॉरवर्ड
  `82`-attr स्कैन के ऊपर, जो §11.8/§11.9 में वर्णित ऑफ-बाय-वन स्रोत है। पूर्ण id तालिका §16 में; ध्यान दें
  कि स्वास्थ्य/मौसम लेबल जो इस अनुभाग ने पहले इनलाइन सूचीबद्ध किए थे (`0x19` HR, `0x1b` बैटरी,
  `0x24` तापमान, `0x36` कदम) **विवादित और संभवतः गलत हैं** — §16 में ⚠️ बॉक्स देखें।
  (नीचे `0x07:0x0b:0x0f` = HH:MM:SS समूह उदाहरण अप्रभावित है.)
- **ग्रुप नोड** (`0x68`): अपने बच्चों को अपने स्वयं के TLV बॉडी के अंदर नेस्ट करता है (`0x60` = मान/टेक्स्ट,
  `0x30` = स्टैटिक); प्रत्येक `0x60` अपना स्रोत id `data+16` पर रखता है। जैसे एक ग्रुप `0x07:0x0b:0x0f` =
  HH:MM:SS घड़ी। TLV तत्व पार्सर = `0x100db55c` (जंप टेबल `tag−0x70` द्वारा अनुक्रमित).

### 11.6 ऑथरिंग मैट्रिक्स

| पथ | स्थिति | नोट्स |
|---|---|---|
| किसी भी इमेज से फोटो डायल | ✅ **पूर्ण** | §11.3; डिवाइस पर मान्य |
| 103 स्टोर डायल में से कोई भी इंस्टॉल करें | ✅ **पूर्ण** | §11.4; `9075`, `old_id`=सक्रिय |
| स्टोर डायल का cf=4 बैकग्राउंड रीस्किन करें | ✅ **लाइव काम करता है** | पूर्ण पेलोड को जगह पर स्वैप करें, एसेट `len` को **नए** ब्लॉक आकार (≤ पुराना) पर सेट करें, समान फ़ाइल फुटप्रिंट रखें, फ्रेश-इंस्टॉल |
| टेम्प्लेटिंग द्वारा पुनः-ऑथर करें (किसी भी परत के पिक्सेल स्वैप करें + ज्यामिति स्थानांतरित करें) | ✅ **BLE के माध्यम से रेंडर होता है** | डायल 373: bg→सियान + एक cf=5 स्प्राइट→लाल + X 224→100 स्थानांतरित, सभी रेंडर, सुइयां लाइव |
| स्क्रैच से 100 %-सिंथेटिक संरचित डायल | ✅ **बिल्डर पूर्ण, ऑफलाइन-मान्य** | `watchface_struct.rs` में `0x20` एनवेलप बिल्डर (`build_container`/`serialize`/`validate_container`); सभी 103 डायल बाइट-एक्सैक्ट राउंड-ट्रिप करता है + सिंथेटिक फर्मवेयर वैलिडेटर (§11.7) पास करता है। 🟡 `9075` पर ऑन-डिवाइस रेंडर अभी फिल्माया नहीं गया |
| सिस्टम फॉन्ट (`.font`) | ✅ **डिकोड/रेंडर (सभी)** | LVGL bin (मालिकाना नहीं); 32 नंबर फॉन्ट (`num*/nm*`, असम्पीडित) + 24 टेक्स्ट फॉन्ट (`font*`, LVGL RLE `comp=1`) सभी डिकोड — 12208 ग्लिफ, 0 ओवररन, पूर्ण ASCII। RLE = LVGL v8.3 `lv_font_fmt_txt.c` (3-स्टेट SINGLE/REPEATE/COUNTER + प्रति-पंक्ति XOR प्रीफिल्टर), 1:1 पोर्टेड, कोई डिसअसेंबली नहीं |

⚠️ रीस्किन/पुनः-ऑथर नुकसान जो ब्लैक स्क्रीन या `0a` का कारण बनते हैं: पुराने **एसेट `len`** को छोड़ना (घड़ी
ब्लॉक से आगे पढ़ती है → ओवररन → ब्लैक); **फ़ाइल को बढ़ाना** (इंस्टॉल पर अस्वीकृत); किसी id को फ्रेश
इंस्टॉल के बजाय **जगह में** पुनः उपयोग करना; रेफ-टेल ब्लॉक-साइज़ श्रृंखला (§11.11) को ठीक किए बिना
**एसेट्स को पुनः क्रमबद्ध करना**। आज घातक नहीं है लेकिन फिर भी इसे सही लिखें: सीन या एसेट पूल में कोई भी
परिवर्तन दो हेडर **CRC32** शब्दों को अमान्य कर देता है — दोनों की पुनर्गणना करें (§11.4), `@0x20` पहले `@0x00`।

### 11.7 `0x20` सीन एनवेलप — डिकोड किया गया और बिल्डर लागू ✅

एक पुनः-ऑथर **वास्तविक** डायल रेंडर होता है क्योंकि यह फ़ाइल के सीन एनवेलप को संरक्षित करता है। एक विशुद्ध सिंथेटिक
फ्लैट `61 …` रिकॉर्ड का बॉडी **अस्वीकार** किया जाता है — फर्मवेयर पार्सर (`WFManager_Parser`, `0xdb35c`)
आवश्यकता है कि बॉडी (ऑफसेट `0x24` से) `0x20` सीन कंटेनर से शुरू हो। पूर्ण फ़ाइल है:```
[0x00,0x24)  header:  perDialId@0 · version=1@4 · name[16]@8 · size_a@0x18 · size_b@0x1c · idWord0@0x20
[0x24, fa)   scene:   20 <u16 L0> ( 21 <u16 L1> ( 86 <len>=name , 30/70/80/81… drawables ) [ 22 … AOD ] )
[fa, EOF)    assets:  [dimsWord u32][len u32][payload = 1f 00 01 00 + LZ4] …
   size_a = filesize−36 · size_b = filesize−36−first_asset · 0x27+L0 == first_asset

दृश्य एक स्वच्छ नेस्टेड TLV है — [tag u8][len u16 LE][body], कंटेनर टैग 0x20/0x21/0x22/0x68 रीकर्स करते हैं, लीफ ड्रॉएबल 0x30 (स्टैटिक) / 0x70 (एलिमेंट/पॉइंटर) / 0x80 / 0x81 / 0x86 (नाम)। (फ्लैट 61 01 00 / 61 0a 00 रिकॉर्ड वे पैटर्न हैं जो ड्रॉएबल बॉडी के अंदर रहते हैं; पुराना पार्सर उन्हें ह्यूरिस्टिक रूप से ढूंढता था — और आसन्न बॉडी को आपस में जोड़ देता था, §11.5 संशोधन देखें। ड्रॉएबल बॉडी लेआउट अब वहाँ पूरी तरह से डिकोड किया गया है।) हर चाइल्ड का offset+len उसके पैरेंट की विंडो के अंदर फिट होना चाहिए; पहला बॉडी बाइट ≠ 0x20 → पार्सर एरर −16; विंडो से बाहर जाने वाला चाइल्ड → −2; दोनों में से कोई भी 9065 हैंडलर (0xeb50c) को फिनिश लिखने पर मजबूर करता है।

बिल्डर लागू और ऑफलाइन-वैलिडेटेड है (core-rust/watchface_struct.rs: SceneNode / serialize / parse_scene / validate_container / build_container / build_container_raw; CLI cmfwatch-wfgen reframe):

  • scene_roundtrip_identity — सभी 103 स्टोर डायल: parse_scene→serialize दृश्य को बाइट-दर-बाइट पुनरुत्पादित करता है (पुनर्गणित नेस्टेड len मेल खाते हैं) और validate_container हर एक पर पास होता है।
  • build_reframe_identity / CLI reframe — पूरे .bin को शुरू से फिर से जोड़ना फ़ाइल को बाइट-दर-बाइट पुनरुत्पादित करता है 1 नाम-पैडिंग बाइट को छोड़कर (@0x17; चेकसम नहीं)।
  • build_container_synthetic — एक नया डायल (बैकग्राउंड + 20→21 में नेस्टेड ड्रॉएबल) बनाता है जो फर्मवेयर के सटीक इनवेरिएंट को पास करता है (build_container सही नेस्टेड विंडो उत्सर्जित करता है)।
  • validate_rejects_bad_containers — एक फ्लैट बॉडी को अस्वीकार करता है (→ , ऐतिहासिक बग) और एक चाइल्ड जो अपनी विंडो से बाहर जाता है (→ )।

🟡 अभी भी अप्रमाणित (वॉच की आवश्यकता है, गैर-अवरोधक): 9075 पर शुरू से सिंथेटिक अपलोड करना और उसे रेंडर होते देखना — ऑफलाइन संरचनात्मक प्रमाण पहले से ही उस चीज़ को कवर करता है जिसने 0a अस्वीकृति का कारण बना।

11.8 रेंडर-फिडेलिटी परिशोधन (2026-07-02, डायल 275 "SlopeTime")

wfweb रेंडर को आधिकारिक स्टोर थंबनेल (सभी 103 डायल पर पिक्सेल ओरेकल) के साथ क्रॉस-रेफरेंस किया और चार अंतराल बंद किए:

  • ड्रॉएबल/पॉइंटर X/Y i16 (साइन्ड) हैं। ✅ एंकर नेगेटिव हो सकते हैं उन एलिमेंट्स के लिए जो कैनवास से बाहर फैलते हैं — जैसे 275 का लाल सेकंड हैंड Y = 0xFFFC = −4 पर बैठता है (एक 30×281 स्प्राइट, स्रोत 0x12, केंद्र से ऊपरी किनारे से घुमाया गया)। X/Y को u16 (65532) के रूप में पढ़ने से गार्ड इसे छोड़ देता था। दोनों को साइन्ड के रूप में पार्स करें और एक छोटी नेगेटिव रेंज की अनुमति दें।
  • डिजिटल क्लॉक अंक टॉप-लेवल 0x60 img_numbers हो सकते हैं (केवल 0x68 ग्रुप के अंदर नहीं), और असली डेटा स्रोत रिकॉर्ड ऑफसेट −5 पर u8 है — फॉरवर्ड 82-एट्र स्कैन यहाँ व्यवस्थित रूप से ऑफ-बाय-वन है और अगले सिबलिंग की एट्र पकड़ लेता है (275 में मिनट अंक ने वीकडे 0x18 उठाया)। 275 का "10:10" = घंटा 0x07@X≈306 + मिनट 0x0b@X≈369 जिसमें उनके बीच एक आसन्न स्टैटिक है, प्रत्येक एक 11-ग्लिफ़ एटलस () है। ⚠️ X/Y को से सही करते समय, , या एक री-एक्सपोर्ट उन बाइट्स को दूषित कर देता है (समान-फुटप्रिंट तोड़ता है → )।

इसके अलावा: आधिकारिक स्टोर थंबनेल 10:10 पर रेंडर किए जाते हैं (क्लासिक मार्केटिंग समय), 10:12 नहीं — ओरेकल समय को 10:10 से मिलाने से मीन पिक्सेल-डिफ़ उल्लेखनीय रूप से गिरता है। wfweb का पार्सर अब सभी 103 डायल को बाइट-एक्सैक्ट राउंड-ट्रिप करता है (ऊपर X/Y राइट-ऑफसेट फिक्स ने अंतिम बेमेल साफ़ किए)।

11.9 AOD-कंटेनर स्किप + स्टैंडअलोन img_number स्रोत (2026-07-03, डायल "Gradient")

  • 0x22 AOD कंटेनर को अपने अलग दृश्य में अलग करें। ✅ सीन वॉकर पहले से ही 0x22 को छोड़ देता है, लेकिन फ्लैट टेक्स्ट/नंबर स्कैन पूरे [0x30, firstAsset) को चलता था — इसलिए यह प्रत्येक एलिमेंट के ऑलवेज़-ऑन (AOD) वेरिएंट को एक सामान्य लेयर के रूप में उत्सर्जित करता था। "Gradient" पर AOD ग्रे डेट एटलस (0x22 में ऑफसेट) सामान्य लाल के ऊपर खींचा गया। फिक्स: हर 0x22 रिकॉर्ड को layer.aod=true के साथ टैग करें (अपने स्वयं के डेडुप सेट के साथ) और renderAt(…, aod) को उन्हें केवल AOD मोड में दिखाने दें (सामान्य मोड aod लेयर छुपाता है; AOD मोड सामान्य लेयर छुपाता है; बैकग्राउंड setAod द्वारा स्वैप किया जाता है और हमेशा खींचा जाता है)। कॉर्पस में सामान्य मोड में शुद्ध ओरेकल जीत (284: 31%→21%, +18 अन्य) — AOD वेरिएंट कई डायल पर ओवरड्रॉ कर रहे थे — और एडिटर का AOD टॉगल अब वास्तविक ऑलवेज़-ऑन लेआउट दिखाता है न कि सामान्य वाले। असली AOD एक काली स्क्रीन है (कोई डिम्ड सीन नहीं): यदि डायल में समर्पित AOD बैकग्राउंड फ्रेम (dial.aod) नहीं है, तो सामान्य सीन AOD मोड में छिपा होता है ताकि यह काला + 0x22 एलिमेंट्स को उनके अपने रंग में रेंडर करे। AOD भी सीन वॉकर के माध्यम से पार्स होते हैं (यह अब कंटेनर में रीकर्स करता है जो ड्रॉएबल्स को टैग करता है, उन्हें फ्लैट स्कैन पर छोड़ने के बजाय जहां उनका पिवट मेल नहीं खाता था → "अनपोज़िशन्ड"); AOD हैंड्स कैनवास केंद्र पर घूमते हैं ( कभी-कभी एक ऑफ-सेंटर हैंड x/y ले जाता है जिसे फर्मवेयर अनदेखा करता है — जैसे Gradient का घंटा )। एडिटर इसे एक के रूप में भी उजागर करता है (§UI): प्रत्येक स्क्रीन केवल अपनी लेयर दिखाता है और संपादन स्वतंत्र रूप से बने रहते हैं। सामान्य-मोड रेंडर पूरे समय बाइट-समान है; राउंडट्रिप सभी 103 डायल पर बाइट-एक्सैक्ट रहता है।

11.10 img_number अंक गणना — 40 01 00 XX बाइट (✅ फर्मवेयर-पुष्टि)

एक img_number कितने अंक खींचता है यह फ़ील्ड रिकॉर्ड में एक एकल बाइट है — एलिमेंट के 40 01 00 XX एट्रिब्यूट सब-रिकॉर्ड का डेटा बाइट XX (वह 0x40 सब-रिकॉर्ड जो 61 [count][base][glyph-ids] फ्रेम टेबल के बाद बैठता है):

  • लो निबल XX & 0x0F = अंक स्लॉट की संख्या (0 ⇒ फर्मवेयर डिफ़ॉल्ट 7)।
  • बिट 7 0x80 = ज़ीरो-पैड (अग्रणी शून्य दिखाएं, जैसे "09" बनाम "9")।

फर्मवेयर को डिसअसेंबल करके पुष्टि की गई (XIP इमेज 0x10000000; रेंडर रूटीन 0x100d8e60): NDIG = ldrb[40sub+3] & 0x0F (→7 यदि 0); मान value % 10^NDIG पर क्लैम्प किया जाता है और ठीक NDIG ग्लिफ़ MS-पहले खींचे जाते हैं, अग्रणी शून्य दबाए जाते हैं जब तक कि bit7 न हो। स्रोत के बाद u16 (डेट के लिए 60, kcal के लिए 1000) गिनती नहीं है — यह केवल हजारों/मिलियन सेपरेटर-ग्लिफ़ सम्मिलन (cmp #1000/#1000000) को फीड करता है, यही कारण है कि इसे संपादित करने से कुछ नहीं हुआ। स्रोत आईडी भी कैप नहीं करती।

सभी 620 नंबर फ़ील्ड पर कॉर्पस हिस्टोग्राम मेल खाता है: 2-अंकीय फ़ील्ड (घंटा/मिनट/सेकंड/डेट/तापमान/HR) समाप्त 40 01 00 02/0x82; kcal …04; कदम …05; एकल-अंकीय क्लॉक स्प्लिट 0x81। तो डेट फ़ील्ड 40 01 00 82 = 2 अंक, ज़ीरो-पैडेड — यही पूरा कारण है कि एक रीबाउंड फ़ारेनहाइट तापमान (≥100) छोटा कर दिया गया था।

फिक्स / एडिटर: wfweb नंबर फ़ील्ड के लिए digitCount/digitZeroPad (+digitCountOff) पार्स करता है, इंस्पेक्टर में "Digits" + "Zero-pad" उजागर करता है, बाइट को इन-प्लेस (समान-फुटप्रिंट) लिखता है, और प्रीव्यू फर्मवेयर को प्रतिबिंबित करने के लिए digitCount पर क्लैम्प/पैड करता है। तो किसी फ़ील्ड के स्रोत को रीबाइंड करना और उसकी अंक गणना सेट करना किसी भी फ़ील्ड के लिए काम करता है (जैसे डेट→तापमान °F → Digits 3)। सामान्य-मोड ओरेकल अपरिवर्तित (0 रिग्रेशन, 3 छोटे सुधार); राउंडट्रिप सभी 103 डायल पर बाइट-एक्सैक्ट। (पहले की "Digits width"/rectW परिकल्पना गलत थी — चौड़ाई केवल लेआउट है, गिनती नहीं।)

11.11 नोड इन्वेंटरी, struct रिकॉर्ड, और संसाधन-संदर्भ टेल ✅

दृश्य (§11.7) एक स्वच्छ नेस्टेड TLV है — [tag u8][len u16 LE][body]। कॉर्पस में देखे गए अनुसार पूर्ण टैग इन्वेंटरी:

✅ यह इन्वेंटरी कॉर्पस के लिए पूर्ण है। केवल उपरोक्त कंटेनर टैग में रीकर्स करते हुए, जांचे गए सभी 15 डायल का सीन TLV अपनी घोषित रूट लंबाई तक ठीक चलता है जिसमें शून्य अज्ञात टैग हैं — इसलिए एक पार्सर जो इस तालिका को संभालता है वह पूरे प्रारूप को संभालता है, और एक अज्ञात टैग का मतलब है एक गलत संरेखित रीड, नई नोड प्रकार नहीं। (सावधान: एक वॉकर जो हर नोड में रीकर्स करता है जिसकी लंबाई संयोग से ≥ 3 है, struct/0x5b बॉडी में उतरेगा और एक-बार के "टैग" की एक लंबी टेल की कल्पना करेगा — लीफ बॉडी TLV नहीं हैं।)

💡 लेखन शॉर्टकट: 0x48/0x68 ऑटो-लेआउट को पूरी तरह से छोड़ा जा सकता है — हर विजेट को स्क्रीन टॉप लेवल पर सीधे निरपेक्ष x,y के साथ रखा जा सकता है, जो शुरू से बिल्डर (§11.7) करता है। केवल मौजूदा डायल पढ़ने के लिए आवश्यक है। 0x8000 की meta चौड़ाई एक struct को फ्रेम के ऑटो-लेआउट चाइल्ड के रूप में चिह्नित करती है (स्थिति पैरेंट से आती है, x,y से नहीं)।

0x01 struct बॉडी — एक निश्चित 18-बाइट उपसर्ग जिसके बाद एक वैकल्पिक संसाधन-संदर्भ टेल है:``` +0x00 x i16 [signed — can be negative, see §11.8] +0x02 y i16 +0x04 meta[14] ──────────────────────────────────────────────────── meta[0..1] w u16 [0x8000 = auto-layout child of a frame] meta[2..3] h u16 meta[4..6] unknown [placeholder-looking (1,0,0)/(4,0,0); see §11.14] meta[7] accent-tint capability flag — 4 = tintable (§11.14) meta[9] DATA SOURCE ID (§16) meta[10] sub / variant meta[11..13] max u24 LE [the metric's nominal full-scale value] +0x12 ref tail [61 …] — absent on imageless rings (0x80/0x81, §11.15)

root@kitploit:~
> ✅ यह §11.5/§11.8/§11.9 के "मैजिक ऑफसेट" को एकीकृत करता है। वे अनुभाग फ़ील्ड को `0x61` फ्रेम-टेबल बाइट के *सापेक्ष* स्थित करते हैं — जो इस struct का केवल `+0x12` है, इसलिए `−18`/`−16` = `x`/`y` और
> **`−5` = `meta[9]`, स्रोत id**। समान बाइट्स, एक साफ लेआउट। आगे-स्कैनिंग `82`-attr
> ह्यूरिस्टिक जो व्यवस्थित रूप से एक-ऑफ थी, की बिल्कुल आवश्यकता नहीं है: struct के `meta[9]` को पढ़ें।
> एक संख्या फ़ील्ड का `max` भी केवल `meta[11..13]` है (जैसे दिन-का-महीना फ़ील्ड `max = 99` रखते हैं)।

**Ref tail** (`61`) — एक नोड अपने बिटमैप्स की ओर कैसे इंगित करता है:```
+0x00  0x61      [tail type]
+0x01  count u16 [1 = single image · 10/11 = digit atlas · N = pick-list / frame sheet]
+0x03  base  u32 [ABSOLUTE FILE OFFSET of the first asset block]
+0x07  count × u16  = the BLOCK SIZE (8 + payload len) of each referenced asset, in order

⚠️ वे पीछे वाले u16s पहले "count×id(u16)" / ग्लिफ़ आईडी के रूप में दस्तावेज़ित किए गए थे। वे ब्लॉक आकार हैं: base, साथ ही उनका चालू योग, एसेट पूल में प्रविष्टि दर प्रविष्टि चलता है (✅ एक डिजिट एटलस की सभी 10 प्रविष्टियों के लिए सटीक सत्यापित)। दो परिणाम:

  • एक नोड के संदर्भित एसेट एसेट पूल में क्रमागत होने चाहिए। कोई रैंडम एक्सेस नहीं है — श्रृंखला केवल आगे बढ़ती है। पूल की योजना बनाएं ताकि प्रत्येक डिजिट सेट (10), प्रत्येक पिक-लिस्ट (N) और प्रत्येक फ्रेम शीट एक सन्निहित ब्लॉक हो। एक लेखक जो श्रृंखला को ठीक किए बिना एसेट्स को पुनः क्रमबद्ध करता है, वह एक फ़ाइल उत्पन्न करता है जो कचरा पढ़ती है (→ काली स्क्रीन)।
  • केवल पहले count−1 आकार भार-वहन करने वाले हैं; अंतिम प्रविष्टि का मान कभी अनुसरण नहीं किया जाता, इसलिए जंगली में फ़ाइलें कभी-कभी वहाँ एक पुराना मान रखती हैं। अंतिम प्रविष्टि पर बेमेल को टूटे हुए संदर्भ के रूप में न मानें।

0x28 पूर्वावलोकन — स्टोर/कैटलॉग थंबनेल .bin में ही एम्बेडेड है (15 डायल्स पर 27 पूर्वावलोकन नोड्स), एक 0x08 pvStruct के रूप में: एक 5-बाइट उपसर्ग प्लस वही रेफ़ टेल, बिना x/y के। अलग PNGs भेजे बिना गैलरी UI बनाने के लिए उपयोगी।

11.12 दृश्यता शर्तें — टैग 0x02 ✅

एक विजेट का 0x02 सहोदर इसे सशर्त बनाता है। इसके बिना, विजेट हमेशा खींचा जाता है। व्याकरण:``` count u8 , count × ( id u8 , op u8 , val u24 LE signed ) [5 bytes per entry]

root@kitploit:~
`id` एक डेटा-स्रोत id है (§16) — जिसमें §11.13 के **सिंथेटिक स्लॉट ids** भी शामिल हैं। ऑपरेटर, जिनकी घटना गिनती 15 डायल्स पर मापी गई है:

| op | अर्थ | देखा गया |
|---|---|---|
| `0x01` | ड्रॉ करें यदि `value == val` | 99 |
| `0x81` | `0x01` के समान (बिट `0x80` सेट — परस्पर-अनन्य वेरिएंट्स पर दिखता है) | 48 |
| `0x02` | **छिपाएँ** यदि `value == val` | 7 |
| `0x03` | ड्रॉ करें यदि `value == val`, जहाँ `val` एक **नो-डेटा मार्कर** है (जैसे HR `1000`) | 13 |
| `0x05` | ड्रॉ करें यदि `value >= val` | 58 |
| `0x06` | ड्रॉ करें यदि `value <= val` | 50 |
| `0x04` | ⚠️ **अज्ञात** — 15 घटनाएँ, कोई पुष्टि किया गया अर्थ नहीं | 15 |

संयोजन नियम (संदर्भ रेंडरर द्वारा लागू, मास्क `op & 0x7f`): समानता प्रविष्टियाँ **OR**-ed होती हैं, फिर hide/`>=`/`<=` प्रविष्टियाँ **सभी** मान्य होनी चाहिए।

यह एक तंत्र प्रारूप की अधिकांश रनटाइम परिवर्तनशीलता को कवर करता है, और उन संरचनाओं की व्याख्या करता है जो डुप्लिकेट विजेट्स जैसी दिखती हैं:

- **12h / 24h और मीट्रिक / इम्पीरियल लेआउट** — एक ही स्थान पर दो विजेट सेट स्टैक किए गए, प्रत्येक `id 0x73` (यूनिट्स फ्लैग) से बंधा हुआ है, जिसमें `val` 0 या 1 है। डायल 275 में ऐसे छह जोड़े हैं (12 नोड्स)।
- **"नो डेटा" प्लेसहोल्डर्स** — op `0x03` एक सेंटिनल के विरुद्ध, जैसे `id 0x5f, val 1000` (डायल 275, दो बार): मीट्रिक अनुपलब्ध होने पर तापमान के बजाय एम-डैश आर्ट ड्रॉ करें।
- **बकेटेड हाइलाइट्स** — एक युग्मित `0x05`/`0x06` रेंज, जैसे Metaball की चेन जहाँ प्रत्येक लिंक अपनी 5-मिनट की विंडो के लिए रोशन होता है।
- **कॉम्प्लिकेशन-स्लॉट विकल्प** — §11.13 के सिंथेटिक स्लॉट ids से बंधे हुए।

### 11.13 कॉन्फ़िगरेबल कॉम्प्लिकेशन स्लॉट्स — `0x85` + `0x5f` ✅ (§11.8 को प्रतिस्थापित करता है)

§11.8 ने निष्कर्ष निकाला था कि एक कॉन्फ़िगरेबल कॉम्प्लिकेशन का सक्रिय मीट्रिक डिवाइस RAM स्थिति है और इसे फ़ाइल से पुनर्प्राप्त नहीं किया जा सकता। **वह गलत था** — स्लॉट का मीट्रिक मेनू और उसका डिफ़ॉल्ट चयन दोनों `.bin` में हैं। प्रत्येक `0x85` नोड एक `0x5f` सिबलिंग रखता है:```
+0x00  slotIndex u8   [0-based position among sibling 0x85 nodes]
+0x01  count     u8   [how many metrics this slot offers]
+0x02  activeIdx u8   [index into the list below = the DEFAULT SHOWN METRIC]
+0x03  count × u8     [the metric ids themselves (§16)]           … NUL padding

वास्तव में जो विकल्प खींचे जाते हैं वे पेड़ में कहीं और सामान्य 0x68 समूह होते हैं, जिनमें से प्रत्येक सिंथेटिक id 0x79 + slotIndex पर एक 0x02 शर्त (§11.12) द्वारा गेट किया जाता है — इसलिए स्लॉट 0 के वेरिएंट 0x79 पर बंधते हैं, स्लॉट 1 के 0x7a पर, और इसी तरह। एक स्लॉट रेंडर करने के लिए: activeIdx पढ़ें, फिर उस वेरिएंट को खींचें जिसकी शर्त उस इंडेक्स से मेल खाती है।

वास्तविक डायल पर मापा गया:

डायल 275 के दो 6-मेट्रिक स्लॉट इसके 26 0x02 नोड्स में से 12 के लिए जिम्मेदार हैं, बिल्कुल अनुमान के अनुसार: 01 79 81 0X 00 00 और 01 7a 81 0X 00 00 for X = 0..5 — 0x79 (स्लॉट 0) पर कुंजीबद्ध छह विकल्प और 0x7a (स्लॉट 1) पर छह। (वे 0x79/0x7a बाइट्स जिन्हें §11.8 ने "instance byte" कहा था, ये bind ids हैं।) इसके शेष 14 असंबंधित हैं: 12 0x73 पर (24h/metric-units फ़्लैग, val 0 या 1 — छह विजेट जोड़े 12h और 24h लेआउट के बीच स्वैप करते हैं) और 2 0x5f पर op 0x03 और val = 1000 के साथ, तापमान no-data प्लेसहोल्डर।

अभी भी वास्तविक डिवाइस स्थिति: उपयोगकर्ता बाद में कंपेनियन ऐप में जो भी चुनता है वह रनटाइम पर activeIdx को ओवरराइड करता है, इसलिए एक प्रीव्यू फ़ाइल डिफ़ॉल्ट को पुन: प्रस्तुत करता है, जरूरी नहीं कि किसी दिए गए घड़ी पर जो दिखता है। एक 0x85 नोड का imgs[0] एक "tap to configure" प्लेसहोल्डर है जिसे फर्मवेयर केवल अपने स्वयं के एडिट मोड में खींचता है — सामान्य समय-प्रदर्शन का प्रीव्यू करते समय इसे छोड़ दें।

11.14 Accent रंग — meta[7] == 4 क्षमता फ़्लैग ✅

कुछ डायल उपयोगकर्ता को डिवाइस पर एक accent रंग चुनने देते हैं, और फर्मवेयर इसे रेंडर समय पर विजेट के बिटमैप में प्रतिस्थापित करता है। स्विच एक एकल बाइट है: struct का meta[7] (§11.11) — यानी 0x01 बॉडी का बाइट +0x0B — 4 के बराबर होना उस विजेट के संसाधन(ओं) को tintable के रूप में चिह्नित करता है।

  • यह एक प्रति-विजेट क्षमता फ़्लैग है, रंग नहीं। किसी फ़्लैग किए गए संसाधन के हर non-transparent पिक्सेल को पुनः रंगें (alpha को अकेला छोड़ दें); इसमें कोई प्रति-पिक्सेल रंग परीक्षण शामिल नहीं है।
  • व्यापकता: यहाँ मापे गए 499 में से 37 structs और 15 में से 8 डायल; fmc कॉर्पस रिपोर्ट करता है 100 में से 56 डायल जिनमें कम से कम एक फ़्लैग किया गया विजेट है।
  • 🛑 निर्यातित बाइट्स में कभी भी accent रंग न बेक करें। प्रतिस्थापन घड़ी पर लाइव है; एक शिप किया गया .bin अपने मूल पिक्सेल रखना चाहिए या आप उपयोगकर्ता की पसंद को स्थायी रूप से खो देंगे। टिंट केवल प्रीव्यू/कैनवास पथ में लागू करें।

⚠️ इसे रंग ह्यूरिस्टिक में "सुधारने" का प्रयास न करें — वह रास्ता एक सिद्ध मृत अंत है (fmc द्वारा कठिन तरीके से करने के बाद प्रलेखित)। सहज सिद्धांत यह है कि फ़्लैग किए गए पिक्सेल किसी पहचानने योग्य प्लेसहोल्डर रंग में बेक किए जाते हैं जिसे फर्मवेयर बदल देता है। यह काम नहीं कर सकता: डायल 348 Tumbler की tintable रिंग और डायल 282 Radar Sweep / 291 Vertical की सामान्य गैर-tintable अंक पट्टियाँ बिल्कुल वही (255,72,32) RGB बेक करती हैं (हर पिक्सेल पर पूरी तरह जाँचा गया); और डायल 305 Dots (घंटे की सुई) और 306 Large Number (अंक) tintable हैं जबकि सादे सफेद बेक किए गए हैं, इसलिए एक रंग परीक्षण उन्हें पूरी तरह से चूक जाएगा। क्रमिक परिशोधन (1 → 4 संदर्भ रंग, साथ ही एक विजेट-भूमिका allowlist) सभी विफल रहे। फ़्लैग पढ़ें।

वास्तविक डिवाइस / कंपेनियन ऐप के विरुद्ध 7 डायल पर क्रॉस-चेक किया गया, दोनों दिशाओं पर ज़ोर देने के लिए चुना गया — 349 Theatre, 376 Digits time, 305 Dots, 306 Large Number, 304 Elaborate 2 सभी accent सेटिंग प्रदान करते हैं और सभी में meta[7]==4 विजेट हैं; 316 Trailing (लाल रंग की सुई, कोई सेटिंग नहीं), 312 Disc और 295 Vortex कोई नहीं प्रदान करते हैं और उनमें शून्य फ़्लैग किए गए विजेट हैं।

meta[4..6] फ़्लैग के ठीक बगल में बैठता है और ऐसा लगता है कि कुछ structs पर यह एक रंग एन्कोड कर सकता है (पूंछ f1=1,f2=255 के साथ एक वास्तविक दिखने वाला RGB, बनाम फ़्लैग किए गए structs के प्लेसहोल्डर (1,0,0)/(4,0,0))। यह accent क्षमता के साथ सहसंबंधित नहीं है। ⚠️ अनसुलझा; इसे अनदेखा करें।

11.15 प्रगति रिंग — 0x80/0x5a (प्रक्रियात्मक) और 0x81/0x5b (छवि-क्लिप किया गया) ✅

दोनों रिंग किस्में एक छोटे struct (x, y, स्रोत id के साथ meta — आमतौर पर कोई ref पूंछ नहीं) को एक arc-spec सहोदर के साथ जोड़ती हैं। §11.5 ने केवल "0x5b: max u16 @+4" प्रलेखित किया, जो एक max i32 का निचला आधा है और स्वीप ज्यामिति को छोड़ देता है। पूरा रिकॉर्ड:``` +0x00 min i32 LE [always 0 in the corpus] +0x04 max i32 LE [100 in the corpus, except dial 332 = 60] +0x08 start i16 LE [sweep start, units of 0.1° — SIGNED] +0x0a end i16 LE [sweep end, units of 0.1° — SIGNED] +0x0c width u16 LE [stroke width in px] +0x0e radius u16 LE [0x5a ONLY — 0x81 takes its radius from the clipped image] +0x0e / +0x10 trailer 01 00 kk ⚠️ unresolved (see below)

root@kitploit:~
युग्मन कठोर है: `0x80` हमेशा ठीक `0x01` + एक **19-बाइट** `0x5a` रखता है, `0x81` हमेशा ठीक
`0x01` + एक **17-बाइट** `0x5b` रखता है (✅ 15 डायल पर 26/26 रिंग)। ⚠️ लेकिन **इमेज-क्लिप्ड `0x81`
हावी है** — उन 26 में से 25। प्रोसीजरल `0x80`/`0x5a` वैरिएंट **एक बार** दिखा (डायल 273), इसलिए इसका
`radius` फ़ील्ड और 19-बाइट लेआउट एक ही नमूने पर टिका है; दोबारा देखे जाने तक संदेह के साथ व्यवहार करें।

`frac = clamp((value − min) / (max − min), 0, 1)`, और भरा हुआ आर्क `start` से `end` की ओर चलता है।
कोण शून्य **3 बजे** पर है, धनात्मक दक्षिणावर्त। मापे गए उदाहरण:

| डायल | टैग | min..max | start → end | चौड़ाई | त्रिज्या |
|---|---|---|---|---|---|
| 273 Activity Mood | `0x5a` | 0..100 | **−102.8° → 102.8°** | 42 | 222 |
| 273 Activity Mood | `0x5b` | 0..100 | 270.0° → 90.0° | 80 | (इमेज) |
| 276 Dichotomy | `0x5b` | 0..100 | 60.0° → −120.0° | 23 | (इमेज) |
| 304 Elaborate 2 | `0x5b` | 0..100 | −2.0° → 358.0° | 24 / 80 | (इमेज) |
| 366 Combo | `0x5b` | 0..100 | 0.0° → 270.0° | 18 | (इमेज) |
| 368 Function | `0x5b` | 0..100 | 0.0° → 360.0° | 20 | (इमेज) |

> ⚠️ **यह §11.5 में "12 बजे से दक्षिणावर्त" धारणा को सुधारता है।** वह केवल विशेष
> मामला है `start = 0, end = 3600` (एक पूर्ण चक्कर, जहाँ परंपरा अप्रत्यक्ष है)। असली डायल
> **आंशिक गेज** (273 का ±102.8° पंखा, 366 का 270° तीन-चौथाई रिंग) और **नकारात्मक चक्कर**
> (276 का 60° → −120°) उपयोग करते हैं, इसलिए एक रेंडरर जो हमेशा ऊपर से पूरा वृत्त घुमाता है वह उन्हें गलत बनाता है।
> 🟡 सटीक कोण-शून्य परंपरा और दिशा नियम fmc के रेंडरर से आते हैं, इन ऑन-डिस्क मानों के
> विरुद्ध क्रॉस-चेक किए गए — इस रिपो द्वारा डिवाइस पर पिक्सेल-दर-पिक्सेल स्वतंत्र रूप से सत्यापित नहीं।
> ध्यान दें `frac = value/max` और §11.5 में `0x81` सेक्टर-क्लिप परिणाम **सत्यापित** किए गए थे (डायल 322)।

⚠️ **अनसुलझा: 3-बाइट ट्रेलर `01 00 kk`।** `kk` प्रशंसनीय मान लेता है (104, 152, 216,
232, 248) और स्पष्ट परिकल्पना एक त्रिज्या है — **परीक्षण किया और खारिज किया**: डायल 366 `kk = 104`
दोनों 82×82 और 166×166 रिंग के लिए उपयोग करता है, और डायल 273 `kk = 232` दोनों 440×440 और 284×284
रिंग के लिए उपयोग करता है। त्रिज्या नहीं, व्यास नहीं। संभवतः अपारदर्शिता/शैली। इसे वैसे ही राउंड-ट्रिप करें।

🟡 **रिपोर्ट की गई समस्या, यहाँ असत्यापित:** fmc के रेंडरर में, एक `0x60` संख्या जिसका स्रोत id उसी स्क्रीन पर
किसी **भी** रिंग के id के बराबर है, कच्चे मान के बजाय `round(frac × 100)` रेंडर करती है — इसलिए
हृदय-गति रिंग के बगल में हृदय-गति संख्या `71 bpm` के बजाय `36` दिखाती है। उनका दस्तावेजीकृत
समाधान रिंग और संख्या को एक ही मीट्रिक के दो **उपनाम** ids पर रखना है (कदम
`0x19`/`0x26`/`0x49`, कैलोरी `0x1c`/`0x1e`/`0x48`)। यह फर्मवेयर व्यवहार है या उनके
रेंडरर के लिए विशिष्ट है — **स्थापित नहीं** — इसके आसपास डिज़ाइन करने से पहले लाइव जाँच के लायक।

---

## 12. बल्क ट्रांसफर और OTA विवरण

ट्रांसफर तालिका §6 में है। अतिरिक्त पुष्टि किए गए बिंदु:

- **AGPS/EPO** ✅: पहला लिखा गया चंक ASCII हेडर `000000010000…` से शुरू होता है। पूर्ण
  init → `[A05F ↔ 905F]×N` → फिनिश लूप वायर पर देखा गया (~892 चंक)।
- **फर्मवेयर OTA** (`9040`–`9042`, फिनिश `9041`) 🔎: संरचना मैप की गई; INIT2 पेलोड = संस्करण बाइट्स
  (जैसे `0b 00 00 39` = 11.0.0.57)। **फ़ील्ड-परीक्षण नहीं** (ऐप यहाँ FW अपडेट अक्षम करता है)। फर्मवेयर
  इमेज **अहस्ताक्षरित प्रतीत होती हैं — अखंडता केवल CRC32 है** (RE में कोई असममित हस्ताक्षर नहीं देखा गया)।
- ⚠️ क्योंकि OTA और `FACTORY_RESET (009A 0001)` प्रमाणित सत्र साझा करते हैं, एक एकल वैध BLE
  प्रमाणीकरण घड़ी को मिटाने या (सिद्धांत रूप में) ब्रिक करने के लिए पर्याप्त है। सावधानी से संभालें।

---

## 13. सेंसर

✅ BLE के माध्यम से हार्डवेयर उजागर:

- **ऑप्टिकल PPG** — हृदय गति (मैनुअल/ऑटो/वर्कआउट/आराम), SpO₂, और HRV-व्युत्पन्न तनाव।
- **3-अक्ष एक्सेलेरोमीटर** — कदम, दूरी, कैलोरी, नींद चरण, कलाई-उठाना, कैडेंस।
- **GNSS/GPS** (AGPS-सहायित) — वर्कआउट ट्रैक (`WORKOUT_GPS`) और स्थान पुश (`GPS_PUSH`)।

कोई **बैरोमीटर/अल्टीमीटर, कंपास, जाइरोस्कोप, या त्वचा/शरीर-तापमान सेंसर** नहीं है। एक आंतरिक
NTC थर्मिस्टर (बोर्ड/बैटरी तापमान) मौजूद है लेकिन केवल AT चैनल के माध्यम से पठनीय है
(`AT GETNTCTEMP`, §14) — इस SKU पर `0155` त्वचा-तापमान इतिहास स्ट्रीम खाली है।

**डेटा-विजेट अपहरण** (कोई वास्तविक कॉम्प्लिकेशन/डेटा-बाइंडिंग API नहीं है — §11.5 देखें): घड़ी के
मौजूदा टेक्स्ट फ़ील्ड को ग्लांसेबल बाहरी डेटा दिखाने के लिए पुनः उपयोग किया जा सकता है। सिद्ध ✅: मौसम
**शहर स्ट्रिंग** (`WEATHER_SET_1`, जैसे `"BRA 2x1 ARG"` विजेट पर दिखाई दी) और संगीत
**ट्रैक/कलाकार** फ़ील्ड; **संपर्क** सूची (20 × नाम[32]+संख्या[25]) एक स्क्रॉल करने योग्य डेटा
पैनल के रूप में काम करती है। सभी पुश हैं, स्थायी कॉम्प्लिकेशन नहीं।

---

## 14. AT फैक्ट्री / शेल चैनल (`77d4ff01` / `77d4ff02`)

फ्रेम किए गए प्रोटोकॉल से स्वतंत्र एक अलग सादा-पाठ AT कमांड चैनल। ✅ लाइव परीक्षण किया गया:

- **पढ़ें:** `AT GETSECRET` (16-बाइट पेयरिंग सीक्रेट), `GETVERSION`, `GETSN`, `GETNAME`, `GETPID`,
  `GETBATLV` (कच्चा mV, जैसे `3853mv`), `GETGSENSOR` (कच्चा एक्सेल g में, `X=… Y=… Z=…`),
  `GETNTCTEMP` (°C, आंतरिक NTC)।
- **लिखें / सक्रिय करें:** `AT SETMOTOR=1` (मोटर कंपन करें), `SETHR/SETHRV/SETSPO2=…` (सेंसर परीक्षण
  इंजेक्शन), `SETLCDSWITCH/SETGPSSWITCH/SETKEYSWITCH`।

उत्तर `,OK` में समाप्त होते हैं। `SET*` कमांड आम तौर पर निष्पादित होते हैं लेकिन BLE पर `,OK` प्रतिध्वनित नहीं कर सकते — मामले के अनुसार पुष्टि करें।

---

## 15. फर्मवेयर-गेटेड / अनुपलब्ध सुविधाएँ (🔎 फर्मवेयर RE)

कुछ सुविधाएँ फर्मवेयर में मौजूद हैं लेकिन SKU/क्षेत्र द्वारा अक्षम हैं और **फोन/BLE से पहुंच योग्य नहीं हैं** —
उन्हें फर्मवेयर मॉड की आवश्यकता है, जो यहाँ दायरे से बाहर है:

- **ChatGPT वॉयस** — गेट = `ux2sys` फीचर id `0x9e`, बूट पर NVRAM/EFUSE/क्षेत्र से सीडेड; इस
  SKU पर सपोर्ट फ्लैग `908b = 00`। फोन, खाते या BLE से प्रभावित नहीं (प्रयोग + RE द्वारा पुष्टि)। ऐप केवल एक रिले है; ऑडियो फोन → Nothing क्लाउड जाता है।
- **रक्तचाप** — फर्मवेयर में एक पूर्ण सबसिस्टम मौजूद है, SKU/क्षेत्र द्वारा बंद।
- **Alipay / NFC भुगतान** — पूर्ण UI मौजूद, केवल चीन-SKU।
- **हार्डवेयर/फर्मवेयर में अनुपस्थित:** ECG, SOS/आपातकाल, सामान्य NFC।

---

## 16. डेटा-स्रोत / कॉम्प्लिकेशन गेटर ids

BLE क्लाइंट बनाने के लिए आवश्यक नहीं, लेकिन **डायल लिखने या रेंडर करने के लिए आवश्यक**: यह `meta[9]` (§11.11) का मान है जो एक विजेट को लाइव डेटा से बांधता है, एक दृश्यता स्थिति का `id` (§11.12), और
एक स्लॉट के मीट्रिक मेनू की प्रविष्टियाँ (§11.13)। फर्मवेयर इसे `0x101f371c` पर **142-प्रविष्टि गेटर
डिस्पैच तालिका** के माध्यम से हल करता है, प्रत्येक प्रविष्टि `ux2sys_get(type)` कहती है (🔎 फर्मवेयर RE)।

### समय / दिनांक — ✅ अच्छी तरह स्थापित

| id | अर्थ | id | अर्थ |
|---|---|---|---|
| `0x01` | घंटा (डिवाइस सेटिंग के अनुसार 12/24 घंटे) | `0x0f`, `0x12` | सेकंड (स्मूथ) |
| `0x04` | घंटा (24 घंटे) | `0x10`, `0x11` | सेकंड दहाई / इकाई |
| `0x07` | घंटा (अनिवार्य 24 घंटे) | `0x71`, `0x72` | सेकंड (टिकिंग / हाथ कोण) |
| `0x02`, `0x03` | घंटा-12 घंटे दहाई / इकाई | `0x13` | AM/PM फ्लैग (0 = AM, 1 = PM) |

Read more

टूल डाउनलोड करें
उद्देश्यसेवाविशेषतागुण
कमांड लिखें0000fff0-0000-1000-8000-00805f9b34fb0000fff2-…Write
कमांड सूचित करें0000fff0-…0000fff1-…Notify
शेल लिखें (AT)—77d4ff01-2fe2-2334-0d35-9ccd078f529cWrite
शेल सूचित करें (AT)—77d4ff02-…Notify
बल्क डेटा लिखें—02f00000-0000-0000-0000-00000000ffe1Write
बल्क डेटा सूचित करें—02f00000-…ffe2Notify
नामcmd1,cmd2
TIMEFFFF 8004
FIRMWARE_VERSION_GET / _RETFFFF 8006 / FFFF 0006
SERIAL_NUMBER_GET / _RET00DE 0002 / 00DE 0001
BATTERY005C 0001
TRIGGER_SYNC005C 0002
USER_INFO_SET / _RET 🔎✅0095 0001 / 0095 0003
FACTORY_RESET009A 0001
DEVICE_REBOOT 🔎FFFF 9080
RESOLUTION_GET 🔎 (→ 466×360)FFFF 907F
GPS_PUSH / _RETFFFF 906A / FFFF A06A
UNBIND_SET / _RETFFFF 907A / FFFF A07A
नामcmd1,cmd2
AUTH_PHONE_NAMEFFFF 8049
AUTH_WATCH_MACFFFF 0049
AUTH_PAIR_REQUEST / _REPLYFFFF 8047 / FFFF 0048
AUTH_NONCE_REQUEST / _REPLYFFFF 804B / FFFF 004C
AUTHENTICATED_CONFIRM_REQUEST / _REPLYFFFF 804D / FFFF 0004
AUTH_FAILEDFFFF A061
नामcmd1,cmd2
APP_NOTIFICATION0065 0001
INCOMING_CALL ⚠️0064 0001
CALL_REMINDER_REQUEST / _RESPONSEFFFF 9066 / FFFF A066
FIND_PHONE005B 0001
FIND_WATCH005D 0001
FIND_WATCH_TOGGLEFFFF 9069
SMS_MESSAGE_PUSH / _RETFFFF 906E / FFFF A06E
QUICK_REPLY_SET / _RETFFFF 9073 / FFFF A073
नामcmd1,cmd2
ALARMS_SET / _GET0063 0001 / 0063 0002
CONTACTS_SET / _GET00D5 0001 / 00D5 0002
STANDING_REMINDER_SET / _GET0060 0001 / 0060 0002
WATER_REMINDER_SET / _GET0061 0001 / 0061 0002
TASK_REMINDER_SET / _RET ⚠️FFFF 9072 / FFFF A072
नामcmd1,cmd2
GOALS_SET / _ACK005E 0001 / 005E 0003
UNIT_LENGTH / _ACKFFFF 9067 / FFFF A067
UNIT_TEMPERATURE / _ACKFFFF 9068 / FFFF A068
TIME_FORMAT / _ACK005F 0001 / 005F 0003
WAKE_ON_WRIST_RAISE / _GET / _ACK0062 0001 / 0062 0002 / 0062 0003
LANGUAGE_SET / _RETFFFF 9058 / FFFF A06B
HEART_MONITORING_ENABLED_SET / _GET009B 0001 / 009B 0002
HEART_MONITORING_ALERTSFFFF 9059
DO_NOT_DISTURB / _GET0099 0001 / 0099 0002
SPORTS_SET / _GET00DC 0001 / 00DC 0002
SPORT_LINKAGE_SET / _RETFFFF 9076 / FFFF A076
SPORT_DATA_SYNC 🔎 (लाइव HR/cal/steps)FFFF 9078 / FFFF A078
FEMALE_CYCLE_SET / _RETFFFF 9071 / FFFF A071
SLEEP_CONFIG_SET / _RET (लक्ष्य मिनट)FFFF 9074 / FFFF A074
WORLD_CLOCK_GETFFFF 906F
WORLD_CLOCK_DST_SET / _RETFFFF 9083 / FFFF A083
VITALITY_GET / _RETFFFF 9079 / FFFF A079
VITALITY_SW_SET / _RETFFFF 9070 / FFFF A070
नामcmd1,cmd2
DIAL_COMMAND_SET / _RET (सूची/पुनःक्रम/चयन)FFFF 9055 / FFFF A055
DIAL_CONFIG_SET / _RETFFFF 9075 / FFFF A075
CHANGE_DIAL (⚠️ 1.0.0.73 पर निष्क्रिय — उपयोग न करें)009F 0001
QUICK_CARD_SET/GET / _RET (दोनों 906D पर)FFFF 906D / FFFF A06D
नामcmd1,cmd2
ACTIVITY_FETCH_1 / _2FFFF 8005 / FFFF 9057
ACTIVITY_FETCH_ACK_1 / _2FFFF 0005 / FFFF A057
ACTIVITY_DATA0056 0001
SLEEP_DATA / _GET0058 0001 / 0058 0002
SPO20055 0001
STRESS009D 0001
HEART_RATE_MANUAL_AUTO0053 0001
HEART_RATE_RESTING00DA 0001
HEART_RATE_WORKOUT00E0 0001
SKIN_TEMP_HISTORY 🔎 (इस SKU पर खाली)0155 0001 / 0155 0002
WORKOUT_SUMMARY / _V30057 0001 / 0160 0001
WORKOUT_GPSFFFF A05A
डोमेनINIT1 req/replyINIT2 req/replyCHUNK req/writeFINISH ack1/ack2
वॉचफेस (फोटो)8052/00529063/A063A064/9064A065/9065
वॉचफेस (संरचित/स्विच)8052/00529075/A075A064/9064A065/9065
फ़र्मवेयर9052/A0529040/A040A042/9042A041/9041
AGPS/EPO905E/A05E—A05F/905FA060/9060
ऑफ़सेटआकारफ़ील्ड
04टाइमस्टैम्प (epoch s)
44कदम
84दूरी (m)
124कैलोरी
1616आरक्षित (0 देखा गया)
ऑफ़सेटआकारफ़ील्ड
04session_start (epoch, UTC)
44wakeup (epoch, UTC)
82total_deep_s
102total_core_s
122total_rem_s
142total_awake_s
162⚠️ [अनिश्चित] (सत्र id/स्कोर? देखे गए मान रिकॉर्ड योग से मेल नहीं खाते)
ac 49 1f 01
  • CONTACTS_SET (00D5 0001) ✅: N × 57 बाइट = name(32) ‖ phone(25)। वॉच UI 20 तक दिखाता है।
  • ALARMS_SET (0063 0001) ✅ — Gadgetbridge को सही करता है (जिसने लेबल को अंत में रखा, 0xff-पैडेड — गलत)। प्रति अलार्म 40 बाइट, बिग-एंडियन: secondsOfDay(i32) ‖ index(u8) ‖ enabled(u8) ‖ repetition-bitmask(u8) ‖ flag(u8) ‖ label[32] UTF-8। लेबल ऑफ़सेट 8 पर है और वॉच पर दिखता है। repetition = सप्ताहांत बिटमास्क (0 = एक-बार); flag ⚠️ [अनिश्चित] है (एक-बार मार्कर?)। उदाहरण (13:30, idx 2): 0000bdd8 02 01 15 00 "Alarm…"।
  • GOALS_SET (005E 0001) ✅ — आधिकारिक ऐप और संदर्भ कार्यान्वयन 10-बाइट, बिग-एंडियन DailyTargetBean v1 का उपयोग करते हैं: steps(u32 BE) ‖ distance_m(u32 BE) ‖ calories_kcal(u16 BE)। (यह Gadgetbridge रूप है; पहले की रिपोर्टें कि वॉच ने इसे "अनदेखा" किया, एक पुराना-सत्र डिक्रिप्ट बग था, पेलोड समस्या नहीं।) 🔎 फ़र्मवेयर RE एक लंबा 29-बाइट विस्तारित वैरिएंट भी दिखाता है (sleep_min/exercise_min/stand_h + 6 सक्षम फ़्लैग जोड़ता है, सभी u32 BE एक flag(u16 LE) उपसर्ग के बाद, सीमाएँ लागू: steps 2000–30000, dist 1000–99000, cal 100–5000, sleep 360–720, exercise 30–90, stand 6–16) — ऐप का डिफ़ॉल्ट पथ नहीं; जब तक अतिरिक्त लक्ष्यों की आवश्यकता न हो, 10-बाइट रूप को प्राथमिकता दें।
  • STANDING_REMINDER / WATER_REMINDER (0060/0061 0001) ✅: 11 बाइट: enabled(1) ‖ threshold_min(u16 LE) ‖ dndStart(u32 LE) ‖ dndEnd(u32 LE)। ध्यान दें कि UI में दिखाई गई "सक्रिय विंडो 08:00–22:00" एक निश्चित फ़र्मवेयर डिफ़ॉल्ट है और पेलोड में नहीं ले जाई जाती।
  • SPORTS_SET (00DC 0001) ✅: count(1) = 36 स्लॉट ‖ activityTypeCode[36] (सक्रिय कोड फिर 00 पैडिंग)। चुनता है कि वॉच के वर्कआउट मेनू में कौन से खेल दिखाई देते हैं।
  • HEART_MONITORING_ENABLED (009B 0001) ✅: kind बाइट — 01 = 24/7 HR, 02 = SpO₂, 04 = तनाव (हर 30 मिनट में मापा जाता है)।
  • HEART_MONITORING_ALERTS (FFFF 9059) ✅: अक्षम = 00; सक्षम = 01 ‖ hrLow ‖ hrHigh ‖ sportHrHigh ‖ spo2Low ‖ 00 00 00 00 (एक 0/255 सीमा = "कोई सीमा नहीं")।
  • FEMALE_CYCLE (FFFF 9071) ✅: 01 ‖ predictionOpen ‖ notifySwitch ‖ cycleStartSwitch ‖ cycleStartNotifyBefore ‖ ovulationStartSwitch ‖ ovulationStartNotifyBefore ‖ fertileStartSwitch ‖ fertileStartNotifyBefore ‖ period(1) ‖ cyclePeriod(1) ‖ cycleStartDate(u32) ‖ markStart(u32) ‖ markEnd(u32) (कैप्चर किया गया: period=5, cyclePeriod=0x1c=28)।
  • QUICK_REPLY (FFFF 9073) ✅: TLV — count(1) ‖ total(1) ‖ [id(1) ‖ len(u16 LE) ‖ msg-UTF8]… (7 डिफ़ॉल्ट उत्तर कैप्चर और डिक्रिप्ट किए गए)।
  • WORLD_CLOCK (FFFF 906F) ✅: नाम नहीं, संख्यात्मक शहर ID भेजता है (01 ‖ count ‖ cityId(2 BE)…); वॉच आंतरिक तालिका से id मैप करती है। DST कॉन्फ़िग FFFF 9083 = count ‖ [id(u16 LE) ‖ dst(u16 LE) ‖ start(u32 LE) ‖ end(u32 LE)]…।
  • MUSIC_INFO_SET (FFFF 905C, 131 B) ✅: state(1: 0=none/1=paused/2=playing) ‖ volume(1) ‖ volumeMax(1) ‖ track(64) ‖ artist(64)। वॉच MUSIC_BUTTON (A05D) भी वापस भेजती है।
  • WEATHER_SET_1 (FFFF 906B, 199 B) ✅ — इसका उपयोग करें: 7×9-बाइट दिन + 24×2-बाइट घंटे + city(32) + 7×8-बाइट सूर्योदय/सूर्यास्त (LE)। तापमान (temp_c + 100) & 0xFF के रूप में एन्कोडेड। ⚠️ वही पेलोड WEATHER_SET_2 (0066 0001) पर भेजा गया Pro 2 पर मौसम विजेट को अपडेट नहीं करता — हमेशा 906B का उपयोग करें। (शहर स्ट्रिंग भी एक सिद्ध डेटा-हाईजैक वेक्टर है — §13 देखें।)
  • FIND_WATCH (005D 0001) ✅: पेलोड 0x01 → वॉच बजती/कंपन करती है (+ ACK 005D 0003)।
  • GPS_PUSH (FFFF 906A) ✅ — बिग-एंडियन, देशांतर पहले: 16 बाइट ts(u32 BE) ‖ lon×1e7(i32 BE) ‖ lat×1e7(i32 BE) ‖ 00 00। वास्तविक स्थान के विरुद्ध मान्य।
  • WORKOUT_GPS (FFFF A05A) ✅ — लिटिल-एंडियन, देशांतर पहले: 12 बाइट ts(i32) ‖ lon×1e7(i32) ‖ lat×1e7(i32)।
  • TIME (FFFF 8004): §7 देखें।
  • cfbppरास्टर (LZ4 के बाद)उपयोग
    42RGB565-LEअपारदर्शी पृष्ठभूमि (FULL/THUMB)
    53RGB565-LE (2 B) + अल्फा (1 B) प्रति pxएंटी-अलियास्ड स्प्राइट्स (ग्लिफ़, हाथ, आइकन)
    13 (0x0d)0.54-बिट अल्फा मास्क; फर्मवेयर रनटाइम पर टिंट करता हैडिजिट-ग्लिफ़ एटलस
    24 (0x18)4RGBA8888फुल-कलर लेयर्स (हमेशा-ऑन aodImage सहित)
    1—JPEG/JFIF (ff d8 ff), किसी भी डिकोडर से निकालेंदुर्लभ एनीमेशन फ्रेम
    0a
    0x61
    NotEnvelope
    0a
    ChildOverflow
    :
    61 0a 00
    −18/−16
    राइट ऑफसेट भी हिलने चाहिए
    0a
  • मल्टी-वेरिएंट कॉम्प्लिकेशन स्लॉट — सक्रिय मेट्रिक .bin में नहीं है ⚠️ यह बुलेट गलत था और §11.13 द्वारा प्रतिस्थापित किया गया है। एक कॉन्फ़िगरेबल कॉम्प्लिकेशन को N 0x68 ग्रुप नोड्स के रूप में लिखा जाता है जो एक ही (x,y) पर स्टैक किए जाते हैं, प्रत्येक केवल तब खींचा जाता है जब एक विज़िबिलिटी शर्त मेल खाती है — इतना सही था। लेकिन प्रति-स्लॉट मेट्रिक सूची (275 का 0x1c/0x6a/0x48/0x24/0x19/0x76) ठीक वही मेट्रिक सूची है, "विकल्प/शैली आईडी" नहीं; डिफ़ॉल्ट सक्रिय इंडेक्स फ़ाइल में एक बाइट है; और 0x79/0x7a बाइट "इंस्टेंस बाइट" नहीं बल्कि वह आईडी है जिस पर विकल्प कुंजीबद्ध हैं (0x79 + slotIndex)। एक स्टैटिक प्रीव्यू कर सकता है फ़ाइल के डिफ़ॉल्ट को पुनरुत्पादित कर सकता है। §11.13 देखें।
  • एज-एंकर्ड निष्क्रिय कॉम्प्लिकेशन (जैसे (446,0) पर bpm टेक्स्ट, 275/302/325/365/375 पर देखा गया) वे स्लॉट हैं जिन्हें फर्मवेयर डिफ़ॉल्ट दृश्य में नहीं खींचता — उनका मान कैनवास किनारे से पहले फिट भी नहीं हो सकता। प्रीव्यू में छिपे हुए के रूप में मानें।
  • हैंड्स
    0x22
    aod
    0x22
    @69,209
    पृथक Normal|AOD संपादन UI
  • स्टैंडअलोन 0x60 img_number (cnt=10) — स्रोत −5 पर, फॉरवर्ड ऑफ-बाय-वन। ✅ §11.8 के समान ऑफ-बाय-वन लेकिन गैर-क्लॉक नंबरों के लिए: "Gradient" की डेट (203,80) टॉप-सेंटर पर स्रोत 0x17 के साथ बैठी थी, लेकिन फॉरवर्ड 82-स्कैन ने पड़ोसी पॉइंटर का एंगल गेटर (0x0a) और पॉइंटर की स्थिति पकड़ ली → नंबर पॉइंटर के स्थान पर एक नकली स्रोत के साथ रेंडर हुआ। फिक्स: एक 61 0a 00 img_number के लिए 0x60 रैपर में, −5/−18/−16 पर भरोसा करें जब फॉरवर्ड स्रोत एक नंबर के लिए असंभव हो (स्रोत-0 या एक पॉइंटर-एंगल गेटर 0x0a/0e/12/70/71/72) और −18/−16 स्थिति मान्य और गैर-शून्य हो (गैर-शून्य गार्ड relX=0 वाले ग्रुप-चाइल्ड अंकों को छोड़ देता है)।
  • 0x17 = डेट (महीने का दिन), 0x24 = तापमान — अलग। डायल 340 दोनों का उपयोग करता है (0x17 "Jun 09" और एक अलग 0x24 तापमान), इसलिए 0x17 डेट है, तापमान नहीं। एक डायल जिसकी वॉच 0x17 स्लॉट में तापमान दिखाती है वह एक उपयोगकर्ता-कॉन्फ़िगर कॉम्प्लिकेशन (डिवाइस स्थिति) है, फ़ाइल डिफ़ॉल्ट नहीं।
  • टैगभूमिकाकंटेनर?बॉडी
    0x20सीन रूट (बॉडी रैपर, ड्रॉएबल नहीं)✅चिल्ड्रन
    0x21सामान्य स्क्रीन✅चिल्ड्रन
    0x22AOD स्क्रीन (§11.9)✅चिल्ड्रन
    0x28एम्बेडेड कैटलॉग प्रीव्यू थंबनेल✅एक 0x08 चाइल्ड
    0x68ग्रुप / ऑटो-लेआउट कंटेनर✅0x48 फ्रेम + चिल्ड्रन
    0x30स्टैटिक इमेज, या N इमेज से मान-द्वारा-पिक✅0x01 (+0x02)
    0x60लाइव न्यूमेरिक रीडआउट (अंक पट्टी)✅0x01 + 0x40 (+0x02)
    0x70घूमता हुआ हैंड✅0x01 + 0x05 पिवट
    0x80प्रोग्रेस रिंग, प्रोसीजरल✅0x01 + 0x5a (§11.15)
    0x81प्रोग्रेस रिंग, इमेज-क्लिप्ड✅0x01 + 0x5b (§11.15)
    0x85उपयोगकर्ता-असाइन करने योग्य कॉम्प्लिकेशन स्लॉट✅0x01 + 0x5f (§11.13)
    0x01struct — ज्यामिति + एट्रिब्यूट्स (नीचे)—x,y,meta[14] + रेफ टेल
    0x02विज़िबिलिटी शर्त (§11.12)—शर्त सूची
    0x05पिवट — flag u8, pivotX u16, pivotY u16—5 B
    0x08pvStruct — prefix[5] + रेफ टेल, कोई x/y नहीं (केवल प्रीव्यू)——
    0x40अंक गणना / ज़ीरो-पैड फ्लैग (§11.10)—1 B
    0x48फ्रेम — x,y,w,h,gap,align ऑटो-लेआउट पंक्ति/स्तंभ——
    0x5a / 0x5b0x80 / 0x81 के लिए आर्क स्पेक (§11.15)—19 B / 17 B
    0x5f0x85 के लिए स्लॉट मेट्रिक सूची (§11.13)——
    0x86डिस्प्ले-नाम नोड, हमेशा ठीक 64 बाइट, NUL-समाप्त, खींचा नहीं गया—64 B
    dialslotcountactiveIdxmetric ids→ active
    275 SlopeTime0601c 6a 48 24 19 760x1c calories
    275 SlopeTime1641c 6a 48 24 19 760x19 steps
    368 Function0805f 1c 19 48 24 76 1a 8b0x5f temperature
    368 Function1865f 1c 19 48 24 76 1a 8b0x1a heart rate
    273 Activity Mood0401c 24 48 6a0x1c calories
    304 Elaborate 20/1401c 48 6a 24 / 24 1c 6a 480x1c / 0x24