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 एन्क्रिप्टेड कमांड फ्रेम्स, प्रमाणीकरण हैंडशेक और वैकल्पिक साथी ऐप विकास हेतु स्वास्थ्य डेटा सिंक का दस्तावेज़ीकरण।

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

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) को डीकंपाइल करके निकाला गया; कोड के अनुरूप लेकिन रनटाइम-परीक्षण नहीं किया गया।
  • ⚠️ [अनिश्चित] — अनुमानित, पुष्टि नहीं; गलत हो सकता है।

सभी कैप्चर के लिए परीक्षण उपकरण: CMF Watch Pro 2-5485, fw 1.0.0.73, serial 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) ले जाता है, जो कमांड चैनल पर नियंत्रण ऑपकोड द्वारा समन्वित होते हैं।

✅ एक पूर्ण वास्तविक सत्र एकल कमांड चैनल पर चला — 160 सेकंड के भारी उपयोग वाले कैप्चर के दौरान स्पष्ट 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 − 2`

✅ सभी देखे गए एन्क्रिप्टेड फ्रेम `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 = वॉच के नॉन्स रिप्लाई से बाइट्स।

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

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


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

दो प्रवेश पथ समान नॉन्स/कन्फर्म टेल साझा करते हैं।

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 इको पैटर्न (✅)

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


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

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

  • 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. Opcode संदर्भ (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-केवल opcodes (FFFF 8051, FFFF 0051, FFFF 90A2, FFFF 90C5, FFFF A056, FFFF 908A/908B ChatGPT स्थिति/समर्थन) को ऐप के हर्मीस बाइटकोड में संभाला जाता है, Java लेयर में नहीं। उनके हेडर कैप्चर में दिखाई देते हैं लेकिन पेलोड अर्थ ⚠️ [अनिश्चित] हैं।

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

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

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


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

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

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

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


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 s में ~170–210 सूचना फ़्रेम धकेलता है। ✅

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

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

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

  • मैनुअल/ऑटो HR, वर्कआउट HR, SpO₂, तनाव = प्रत्येक 8 बाइट्स: टाइमस्टैम्प(i32 LE) ‖ मान(i32 LE) (मान = bpm / SpO₂ % / तनाव सूचकांक)।
  • विश्राम HR (00DA 0001) अलग है — 5 बाइट्स: टाइमस्टैम्प(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-बाइट रिकॉर्ड: टाइमस्टैम्प(u32) ‖ अवधि_s(u16) ‖ अवस्था(u16). अवस्था कोड: 1 = गहरी, 2 = मध्य/हल्की, 3 = REM, 4 = जागृत। ✅ एक पूरी रात के विरुद्ध मान्य किया गया (दो सत्र, D/C/R/A योग मेल खाते हैं)।

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

v1: आरंभ(u32), अंत(u32), अवधि_s(u32), फिर प्रकार/कैलोरी/कदम/दूरी/औसत-HR और एक GPS/विस्तारित ब्लॉक। ✅ v1 लेआउट फ़र्मवेयर के विरुद्ध पुष्टि की गई। WORKOUT_SUMMARY_V3 उसी डेटा के लिए एक नया लेआउट है और एक ~40-बाइट विस्तारित ब्लॉक (व्यायामभार, एरोबिक/एनारोबिक, रिकवरीटाइम, VO₂max, कैडेंस, 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) ✅: उत्तर = स्तर(1) ‖ चार्जिंग(1) (जैसे 3b 00 = 59 %, चार्ज नहीं हो रहा)।
  • SERIAL_NUMBER_RET (00DE 0001) ✅: len(1) ‖ ASCII (जैसे 10 + "CI04102520008192")।
  • USER_INFO (0095 0001) ✅: ऊंचाई_सेमी(1) ‖ वजन_किग्रा(1) ‖ आयु(1) ‖ लिंग(1: 1=M) (जैसे = 172 सेमी / 73 किग्रा / 31 / पुरुष)।

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

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

11. घड़ी के चेहरे / डायल — लेखन

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

वास्तव में क्या काम करता है (✅ लाइव मान्य): किसी भी छवि से फ़ोटो डायल बनाना और उसे इंस्टॉल करना; 103 स्टोर डायल को ऑफ़लाइन इंस्टॉल करना; एक संरचित डायल को रीस्किन करना (पृष्ठभूमि या किसी भी गैर-पृष्ठभूमि स्प्राइट को बदलना) और इसकी परतों को स्थानांतरित करना; सक्रिय चेहरे को पुनःक्रमित / स्विच करना; और शुरू से एक संरचित डायल बनाना — 0x20 दृश्य लिफाफा डिकोड किया गया है और बिल्डर कार्यान्वित किया गया है (§11.7), सभी 103 स्टोर डायल को बाइट-दर-बाइट राउंड-ट्रिप करने और सिंथेटिक कंटेनरों का उत्सर्जन करने के लिए ऑफ़लाइन सिद्ध किया गया है जो फ़र्मवेयर के अपने वैलिडेटर को पास करते हैं। 🟡 एकमात्र अप्रमाणित कदम 9075 पर उपकरण पर एक खरोंच-से-सिंथेटिक रेंडर देखना है (संरचनात्मक ऑफ़लाइन प्रमाण पहले से ही उस चीज़ को कवर करता है जो 0a अस्वीकार का कारण बनती थी)। कोई कोडेक या परिवहन बाधा नहीं है और विक्रेता टूलचेन की कोई आवश्यकता नहीं है। पुराने "संरचित रेंडर RES-pack-बेक्ड / BLE पर असंभव है" और "cf=0x1f सर्वर-साइड कोडेक" दावे गलत थे (एक ऑफ़सेट+बाइट्स-प्रति-पिक्सेल बग) — फ़र्मवेयर संरचित डायल को आपके द्वारा भेजी गई फ़ाइल से डेटा-संचालित रेंडर करता है।

11.1 डायल प्रबंधन — DIAL_COMMAND (9055 / a055) ✅

  • type 0 = सूची क्वेरी करें। उत्तर a055 = result(u8) ‖ selectIndex(u8) ‖ total(u8) ‖ max(u8) ‖ N × dialId(u32 LE) ‖ ffffffff। उदाहरण: 01 05 06 07 … = सक्रिय #5, 6 डायल, अधिकतम 7।
  • type 1 = पुनःक्रमित / सक्रिय चयन करें: पूरी सूची को फिर से भेजें जिसमें लक्ष्य डायल इंडेक्स 0 पर हो (यह आधिकारिक ऐप चेहरे बदलने का तरीका है; कोई समर्पित "सेट सक्रिय" opcode नहीं है)।
  • एक डायल हटाएं = सूची को उसकी आईडी के बिना फिर से भेजें।
  • CHANGE_DIAL (009F 0001) fw 1.0.0.73 पर निष्क्रिय है (एक स्थिरांक लौटाता है, स्विच नहीं करता) — इसका उपयोग न करें।

11.2 स्थानांतरण प्रवाह ✅```

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`)। पहले से मौजूद किसी आईडी को पुनः स्थापित करने के लिए, **पहले इसे हटाएं** (9055 list-minus-id) फिर "नया" अपलोड करें — स्थान पर किसी आईडी का पुन: उपयोग करने पर `0a` मिलता है।

### 11.3 फोटो / कस्टम डायल — ✅ fully validated end-to-end

**Container** (बाइट-सत्यापित राउंड-ट्रिप; सभी फ़ील्ड लिटिल-एंडियन):```
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]

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

INIT_2 for 9063 — सटीक हेडर (✅ यह वही है जो काम करता है):``` 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` रूप समाप्ति `0a` के साथ **अस्वीकृत** होता है — ऊपर दिए गए पूर्ण हेडर का उपयोग करें। (संदर्भ कार्यान्वयन: `core-rust/engine.rs::build_wf_init2`, आधिकारिक ऐप में `C6135t.m31104u` की प्रतिकृति।)

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

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

**हेडर** (सभी 103 स्टोर डायल में समान; सभी फ़ील्ड लिटिल-एंडियन):```
0x00  perDialId  u32 LE     [per-dial id/hash; NOT a content checksum — 4 "Default" dials share one]
0x04  version    0x00000001 [constant]
0x08  name       char[]     [NUL-terminated, e.g. "SlopeTime", "Metaball"]
0x18  size_a     u32 LE     [= filesize − 36]   ✅ 100 % confirmed across 103 dials
0x1c  size_b     u32 LE     [data-section length, < size_a]
0x20  3× u32 LE  id/hash words [not a CRC]
0x2c  name       (repeated on larger dials)
~0x60 directory of layer records (61 xx 00 …) then the asset pool

कोई ब्लॉकिंग चेकसम नहीं है (CRC32/Adler32/byte-sum सभी मेल नहीं खाते) — रीपैकिंग पर कोई रोक नहीं है। स्टब डायल (~173 B, उदा. आईडी 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:

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

9075 के लिए INIT_2 — AES-encrypted body:``` 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 आईडी 359 +
102 अन्य पुष्ट). (संदर्भ: `core-rust/engine.rs::build_dial_replace_init`.)

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

> **⚠️ संशोधन (2026-07-02): नीचे दिया गया सपाट `61 01 00` रिकॉर्ड स्कीमा व्यवस्थित रूप से
> ऑफ-बाय-वन था।** दृश्य बॉडी एक स्वच्छ TLV (§11.7) है; एक ड्रॉ करने योग्य **लीफ बॉडी** (टैग `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 = top-left** 466² कैनवास पर (SDK के `sty_picture_t` का `s16 x,y`)।
> - **फ्रेम तालिका `61 …` बॉडी को बंद करती है** (`base` = एसेट ptr; `count` 1 = छवि, 10/11 =
>   अंक एटलस — पुराना "रिकॉर्ड प्रकार `0a/0b`" वास्तव में यही count था! — 7/13/2 = कॉम्प्लिकेशन
>   फ्रेम शीट)।
> - पॉइंटर एक्स्ट्रा: `0x01` attr के अंदर source+scale `[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") से जोड़ रहा था — यह केवल एनालॉग डायल पर *सही* दिखता था जिनके
> आसन्न हाथों की ज्यामिति लगभग समान होती है। "कॉम्पैक्ट वेरिएंट वॉल" (स्पेक 24 §24.4.5) यही
> गलत पढ़ाई था। `core-rust/watchface_struct.rs` और `wfweb/src/codec/parse.ts` में `scan_scene_drawables` के रूप में कार्यान्वित किया गया (दृश्य = छवियों/पॉइंटर्स के लिए प्राथमिक स्रोत; टेक्स्ट + गैर-लिफाफा फॉलबैक के लिए सपाट स्कैन रखा गया)। `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² कैनवास पर top-left = `(X−pivotX, Y−pivotY)`।
- **पॉइंटर/हैंड** — समान छवि रिकॉर्ड, रनटाइम पर घुमाया जाता है। **रोटेशन केंद्र = `(X+pivotX, Y+pivotY)`**
  (≈ 233,233 एनालॉग डायल पर)। **डेटा स्रोत रिकॉर्ड ऑफ़सेट `+36` पर एक `u8` है**, स्केल `u16` `+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 ‖ flag ‖
  3B ‖ 01 ‖ u16 ‖ X(u16) ‖ Y(u16)`. `asset_ptr` ग्लिफ़ "0" की ओर इंगित करता है; अंक *d* = `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, **स्पेक 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` (`max` u16 `@+4`, =100 सिवाय 332=60)। wfweb (`blendSector`) में कार्यान्वित किया गया।
- **डेटा स्रोत आईडी** — तत्व का `82` attr-ब्लॉक `delim+3` पर बैठता है (अंतिम `40 01 00` के बाद), और **स्रोत आईडी `+0x14` पर एक `u8` है** (साथ ही `relX@+0x07 s16`, `relY@+0x09 s16`, `anchor@+0x0C/0E`, `mode@+0x15`, `frame-count@+0x1A`). Anchor < 0 = मूल के किनारे पर संरेखित करें। 🔎 फर्मवेयर आईडी को `0x101f371c` पर 142-प्रविष्टि गेटर तालिका के माध्यम से हल करता है (प्रत्येक `ux2sys_get(type)` कहता है)। सामान्य आईडी (§16): `0x07` घंटा, `0x0b` मिनट, `0x0f` सेकंड, `0x16` माह, `0x18` सप्ताह का दिन, `0x13` AM/PM, `0x19` HR, `0x1b` बैटरी %, `0x24` तापमान, `0x36` कदम, `0x70/71/72` हाथ कोण, `0x25–27` लक्ष्य %. (यह नीचे `0x07:0x0b:0x0f` = HH:MM:SS समूह उदाहरण से मेल खाता है।)
- **समूह नोड** (`0x68`): अपने बच्चों को अपने स्वयं के TLV बॉडी के अंदर नेस्ट करता है (`0x60` = मान/टेक्स्ट, `0x30` = स्थिर); प्रत्येक `0x60` अपनी स्रोत आईडी `data+16` पर रखता है। उदा. एक समूह `0x07:0x0b:0x0f` = HH:MM:SS घड़ी। TLV तत्व पार्सर = `0x100db55c` (`tag−0x70` द्वारा अनुक्रमित जंप तालिका)।

### 11.6 लेखक मैट्रिक्स

| पथ | स्थिति | नोट्स |
|---|---|---|
| किसी भी छवि से फोटो डायल | ✅ **हो गया** | §11.3; डिवाइस पर मान्य किया गया |
| 103 स्टोर डायलों में से किसी को स्थापित करें | ✅ **हो गया** | §11.4; `9075`, `old_id`=सक्रिय |
| स्टोर डायल के cf=4 पृष्ठभूमि को फिर से स्किन करें | ✅ **लाइव काम करता है** | FULL पेलोड को उसी स्थान पर बदलें, एसेट `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`** को छोड़ना (घड़ी ब्लॉक से आगे पढ़ती है → ओवररन → ब्लैक); **फ़ाइल को बड़ा करना** (स्थापना पर अस्वीकृत); किसी आईडी को **उसी स्थान पर** पुन: उपयोग करना न कि ताज़ा स्थापना के रूप में।

### 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 दृश्य को बाइट-दर-बाइट पुन: उत्पन्न करता है (पुनर्गणित नेस्टेड lens मेल खाते हैं) और 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 मोड में सामान्य दृश्य छिपा दिया जाता है ताकि यह काला + एलिमेंट्स को उनके स्वयं के रंग पर रेंडर करे। AOD भी सीन वॉकर के माध्यम से पार्स होते हैं (यह अब कंटेनर को पुनरावृत्त करते हुए ड्रॉएबल्स को टैग करता है, उन्हें फ्लैट स्कैन पर छोड़ने के बजाय जहां उनका पिवोट मेल नहीं खाता था → "अनपोज़िशन्ड"); AOD हैंड्स कैनवास केंद्र पर घूमते हैं (द कभी-कभी एक ऑफ-सेंटर हैंड x/y रखता है जिसे फर्मवेयर अनदेखा करता है — जैसे Gradient का घंटा )। एडिटर इसे एक के रूप में भी उजागर करता है (§UI): प्रत्येक स्क्रीन केवल अपनी स्वयं की लेयर दिखाता है और संपादन स्वतंत्र रूप से बने रहते हैं। सामान्य-मोड रेंडर पूरे समय बाइट-समान है; राउंडट्रिप सभी 103 डायल्स पर बाइट-सटीक रहता है।

11.10 img_number DIGIT COUNT — 40 01 00 XX बाइट (✅ फर्मवेयर-पुष्टि)

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

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

फर्मवेयर को डिस्सेम्बल करके पुष्टि की गई (XIP इमेज 0x10000000; रेंडर रूटीन 0x100d8e60): NDIG = ldrb[40sub+3] & 0x0F (→7 यदि 0); मान को value % 10^NDIG में क्लैंप किया गया है और बिल्कुल NDIG ग्लिफ़ MS-पहले खींचे जाते हैं, प्रमुख शून्य दबाए जाते हैं जब तक कि बिट7 न हो। स्रोत के बाद 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 परिकल्पना गलत थी — चौड़ाई केवल लेआउट है, गिनती नहीं।)


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

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

  • AGPS/EPO ✅: पहला लिखा गया चंक ASCII हेडर 000000010000… से शुरू होता है। पूर्ण आरंभीकरण → [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 × name[32]+number[25]) एक स्क्रॉल करने योग्य डेटा पैनल के रूप में काम करती है। सभी पुश हैं, स्थायी कॉम्प्लिकेशन नहीं।


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

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

  • पढ़ें: AT GETSECRET (16-बाइट पेयरिंग सीक्रेट), GETVERSION, GETSN, GETNAME, GETPID, GETBATLV (कच्चा mV, जैसे 3853mv), GETGSENSOR (कच्चा accel 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 फीचर आईडी 0x9e, बूट पर NVRAM/EFUSE/क्षेत्र से सीडेड; इस SKU पर समर्थन ध्वज 908b = 00। फोन, खाते या BLE से प्रभावित नहीं किया जा सकता (प्रयोग + RE द्वारा पुष्टि की गई)। ऐप केवल एक रिले है; ऑडियो फोन → Nothing क्लाउड जाता है।
  • रक्तचाप — फर्मवेयर में एक पूर्ण उपप्रणाली मौजूद है, SKU/क्षेत्र द्वारा बंद।
  • Alipay / NFC भुगतान — पूर्ण UI मौजूद है, केवल चीन-SKU।
  • हार्डवेयर/फर्मवेयर में अनुपस्थित: ECG, SOS/आपातकालीन, सामान्य NFC।

16. कॉम्प्लिकेशन गेटर तालिका (🔎 फर्मवेयर-आंतरिक संदर्भ)

BLE क्लाइंट बनाने के लिए आवश्यक नहीं — पूर्णता के लिए शामिल। फर्मवेयर का डायल रेंडरर प्रत्येक कॉम्प्लिकेशन स्लॉट को एक संख्यात्मक गेटर आईडी (142-प्रविष्टि डिस्पैच टेबल) से बांधता है। चयनित आईडी: 0x07 घंटा, 0x0a संयुक्त घड़ी कोण, 0x0b मिनट, 0x0f सेकंड, 0x18 सप्ताह का दिन, 0x19 हृदय गति, 0x1b बैटरी %, 0x24 तापमान, 0x36 कदम, 0x70/0x71/0x72 घंटा/मिनट/सेकंड हैंड कोण, 0x25–0x27 लक्ष्य %। रिंग/आर्क कॉम्प्लिकेशन एक प्री-रेंडर्ड फ्रेम शीट को इंडेक्स करते हैं (जैसे 50% = 100 में से फ्रेम 50), प्रति-पिक्सेल आर्क नहीं — फ्रेम्स आपके द्वारा भेजे गए .bin में बेक किए गए हैं (§11.5), इसलिए किसी बाहरी RES पैक की आवश्यकता नहीं है।


क्विक-कार्ड (होम टाइल्स) — QUICK_CARD (906D) ✅

घड़ी के होम टाइल्स। फोन केवल यह चुनता है कि कौन से टाइल दिखाने हैं और किस क्रम में — टाइल्स फर्मवेयर द्वारा रेंडर किए जाते हैं (कोई सामग्री चैनल नहीं)। पहला पेलोड बाइट = उप-कमांड: 00 = GET, 01 = SET; दोनों 0x906D का उपयोग करते हैं (0x906C सूचीबद्ध है लेकिन उपयोग नहीं किया गया — इसे क्वेरी करने पर टाइमआउट होता है)। उत्तर = A06D।

🛑 एक मनगढ़ंत assemblyId भेजने से घड़ी के स्क्रीन मिट जाते हैं (यह सूची स्वीकार करता है, आईडी से मेल नहीं खा सकता, कुछ नहीं दिखाता)। केवल वे आईडी भेजें जिन्हें आप GET के माध्यम से वापस पढ़ते हैं; आधिकारिक ऐप या फ़ैक्टरी रीसेट के माध्यम से पुनर्प्राप्त करें।

GET उत्तर ✅: status(1) ‖ 00 ‖ N(1) ‖ N × समूह, समूह = tag=01 ‖ K(1) ‖ K×(assemblyId, sportId)। वास्तविक फ्रेम: 01 00 04 01 02 5d00 6100 01 03 1900 2e00 2300 01 03 5c00 0400 5a02 01 03 4800 5100 5300 = 4 स्क्रीन / 11 कार्ड (5a02 = स्पोर्ट कार्ड, sportId 2)।

स्लॉट: प्रत्येक स्क्रीन में 4 स्लॉट हैं। कार्ड का प्रकार उसका आकार निर्धारित करता है — circular/square = 1 स्लॉट, rectangle = 2 स्लॉट। मान्यता शुद्ध स्लॉट अंकगणित है (Σ ≤ 4 प्रति स्क्रीन); कोई परस्पर अनन्य कार्ड नहीं। sportId स्पोर्ट कार्ड (87–91) को छोड़कर 0 है। आईडी 64 और 95 मौजूद नहीं हैं।

assemblyId सूची (प्रत्येक तार्किक प्रकार = 6 शैली प्रकार _0.._5 का एक सन्निकट श्रेणी; 0 = खाली स्लॉट):


उपरोक्त बाइट लेआउट फर्मवेयर (1.0.0.73), आधिकारिक APK (3.5.7), और एक वास्तविक डिवाइस के विरुद्ध डिक्रिप्टेड लाइव कैप्चर से पुनर्निर्मित किए गए थे। इस प्रोजेक्ट के लिए संदर्भ कार्यान्वयन core-rust/src/{commands,frame,crypto,health,session}.rs (Rust) और cmftool/ Python टूल्स (pair.py, session.py, wf_codec.py, upload_custom.py, …) में रहता है।

टूल डाउनलोड करें
उद्देश्यसेवाविशेषतागुण
कमांड लिखें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/कदम)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 अनुरोध/उत्तरINIT2 अनुरोध/उत्तरCHUNK अनुरोध/लिखेंFINISH ack1/ack2
वॉचफ़ेस (फ़ोटो)8052/00529063/A063A064/9064A065/9065
वॉचफ़ेस (संरचित/स्विच)8052/00529075/A075A064/9064A065/9065
फ़र्मवेयर9052/A0529040/A040A042/9042A041/9041
AGPS/EPO905E/A05E—A05F/905FA060/9060
ऑफ़सेटआकारफ़ील्ड
04टाइमस्टैम्प (सेकंड में युग)
44कदम
84दूरी (मीटर)
124कैलोरी
1616आरक्षित (0 देखा गया)
ऑफ़सेटआकारफ़ील्ड
04सत्र_आरंभ (युग, UTC)
44जागरण (युग, UTC)
82कुल_गहरा_s
102कुल_मध्य_s
122कुल_REM_s
142कुल_जाग_s
162⚠️ [अनिश्चित] (सत्र आईडी/स्कोर? देखे गए मान रिकॉर्ड योग से मेल नहीं खाते)
ac 49 1f 01
  • CONTACTS_SET (00D5 0001) ✅: N × 57 बाइट्स = नाम(32) ‖ फ़ोन(25)। घड़ी UI 20 तक दिखाता है।
  • ALARMS_SET (0063 0001) ✅ — Gadgetbridge को सही करता है (जिसने लेबल को अंत में रखा, 0xff-पैडेड — गलत)। प्रति अलार्म 40 बाइट्स, बिग-एंडियन: दिन_का_सेकंड(i32) ‖ सूचकांक(u8) ‖ सक्षम(u8) ‖ पुनरावृत्ति-बिटमास्क(u8) ‖ फ़्लैग(u8) ‖ लेबल[32] UTF-8। लेबल ऑफ़सेट 8 पर है और घड़ी पर दिखता है। पुनरावृत्ति = सप्ताह के दिन की बिटमास्क (0 = एक बार); फ़्लैग ⚠️ [अनिश्चित] है (एक-बार मार्कर?)। उदाहरण (13:30, idx 2): 0000bdd8 02 01 15 00 "Alarm…"।
  • GOALS_SET (005E 0001) ✅ — आधिकारिक ऐप और संदर्भ कार्यान्वयन 10-बाइट, बिग-एंडियन DailyTargetBean v1 का उपयोग करते हैं: कदम(u32 BE) ‖ दूरी_m(u32 BE) ‖ कैलोरी_kcal(u16 BE)। (यह Gadgetbridge रूप है; पहले की रिपोर्ट कि घड़ी ने इसे "अनदेखा" किया एक पुराने-सत्र डिक्रिप्ट बग था, पेलोड समस्या नहीं।) 🔎 फ़र्मवेयर RE एक लंबा 29-बाइट विस्तारित प्रकार भी दिखाता है (जोड़ता है sleep_min/exercise_min/stand_h + 6 सक्षम फ़्लैग, सभी u32 BE एक flag(u16 LE) उपसर्ग के बाद, रेंज प्रवर्तन के साथ: कदम 2000–30000, दूरी 1000–99000, कैल 100–5000, नींद 360–720, व्यायाम 30–90, खड़ा 6–16) — यह ऐप का डिफ़ॉल्ट पथ नहीं है; जब तक आपको अतिरिक्त लक्ष्यों की आवश्यकता न हो, 10-बाइट फ़ॉर्म पसंद करें।
  • STANDING_REMINDER / WATER_REMINDER (0060/0061 0001) ✅: 11 बाइट्स: सक्षम(1) ‖ सीमा_मिनट(u16 LE) ‖ dndStart(u32 LE) ‖ dndEnd(u32 LE)। ध्यान दें UI में दिखाई गई "सक्रिय विंडो 08:00–22:00" एक निश्चित फ़र्मवेयर डिफ़ॉल्ट है और पेलोड में नहीं ले जाया जाता है।
  • SPORTS_SET (00DC 0001) ✅: गिनती(1) = 36 स्लॉट ‖ गतिविधि प्रकार कोड[36] (सक्रिय कोड फिर 00 पैडिंग)। चुनता है कि घड़ी के वर्कआउट मेनू में कौन से खेल दिखाई दें।
  • HEART_MONITORING_ENABLED (009B 0001) ✅: प्रकार बाइट — 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 — गिनती(1) ‖ कुल(1) ‖ [id(1) ‖ len(u16 LE) ‖ msg-UTF8]… (7 डिफ़ॉल्ट उत्तर कैप्चर और डिक्रिप्ट किए गए)।
  • WORLD_CLOCK (FFFF 906F) ✅: संख्यात्मक शहर आईडी भेजता है, नाम नहीं (01 ‖ गिनती ‖ cityId(2 BE)…); घड़ी आंतरिक तालिका से आईडी मैप करती है। DST कॉन्फ़िग FFFF 9083 = गिनती ‖ [id(u16 LE) ‖ dst(u16 LE) ‖ start(u32 LE) ‖ end(u32 LE)]…।
  • MUSIC_INFO_SET (FFFF 905C, 131 B) ✅: स्थिति(1: 0=कोई नहीं/1=रोका/2=चल रहा) ‖ वॉल्यूम(1) ‖ volumeMax(1) ‖ ट्रैक(64) ‖ कलाकार(64)। घड़ी वापस MUSIC_BUTTON (A05D) भी भेजती है।
  • WEATHER_SET_1 (FFFF 906B, 199 B) ✅ — इसका उपयोग करें: 7×9-बाइट दिन + 24×2-बाइट घंटे + शहर(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।
  • cfbppraster (LZ4 के बाद)उपयोग
    42RGB565-LEअपारदर्शी पृष्ठभूमि (FULL/THUMB)
    53प्रति px RGB565-LE (2 B) + अल्फा (1 B)anti-aliased स्प्राइट्स (ग्लिफ़, हाथ, आइकन)
    13 (0x0d)0.54-बिट अल्फा मास्क; फर्मवेयर runtime पर रंग टिंट करता हैdigit-glyph एटलस
    24 (0x18)4RGBA8888पूर्ण-रंग परतें (incl. हमेशा चालू रहने वाली aodImage)
    1—JPEG/JFIF (ff d8 ff), किसी भी डिकोडर से निकालेंदुर्लभ एनिमेशन फ्रेम
    0a
    0x61
    NotEnvelope
    0a
    ChildOverflow
    :
    61 0a 00
    −18/−16
    राइट ऑफसेट भी स्थानांतरित होने चाहिए
    0a
  • मल्टी-वेरिएंट कॉम्प्लिकेशन स्लॉट: सक्रिय मीट्रिक .bin में नहीं है। ⚠️ एक कॉन्फ़िगरेबल कॉम्प्लिकेशन को N 0x68 समूह नोड्स के रूप में एक ही (x,y) पर स्टैक किया गया, प्रत्येक एक अलग स्रोत से बंधा हुआ (275 के दो वृत्त: प्रति स्लॉट 0x1e/0x6a/0x48/0x24/0x19 — विकल्प/शैली आईडी, न कि दिखाया गया मीट्रिक); दो वृत्त अपने आयत + एक इंस्टेंस बाइट (0x79/0x7a) के अलावा बाइट-समान हैं। कौन सा मीट्रिक दिखता है (STEPS बनाम KCAL बनाम …) डिवाइस RAM/कॉन्फ़िग स्थिति है, इसलिए एक स्थैतिक पूर्वावलोकन इसे फ़ाइल से पुन: उत्पन्न नहीं कर सकता — केवल सर्वोत्तम प्रयास।
  • एज-एंकर्ड निष्क्रिय कॉम्प्लिकेशन (जैसे (446,0) पर bpm टेक्स्ट, जो 275/302/325/365/375 पर देखा गया) वे स्लॉट हैं जिन्हें फर्मवेयर डिफ़ॉल्ट व्यू में नहीं खींचता — उनका मान कैनवास किनारे से पहले फिट भी नहीं हो सकता। पूर्वावलोकन में छिपा हुआ मानें।
  • 0x22
    हैंड्स
    0x22
    aod
    0x22
    @69,209
    पृथक सामान्य|AOD संपादन UI
  • स्टैंडअलोन 0x60 img_number (cnt=10) — स्रोत −5 पर, आगे ऑफ-बाय-वन। ✅ §11.8 के समान ऑफ-बाय-वन लेकिन गैर-घड़ी संख्याओं के लिए: "Gradient" की तिथि शीर्ष-केंद्र पर (203,80) पर बैठी थी जिसका स्रोत 0x17 था, लेकिन आगे 82-स्कैन ने पड़ोसी पॉइंटर के कोण गेटर (0x0a) को पकड़ लिया और पॉइंटर की स्थिति → संख्या पॉइंटर के स्थान पर एक बोगस स्रोत के साथ रेंडर हुई। फिक्स: 0x60 रैपर में 61 0a 00 img_number के लिए, −5/−18/−16 पर भरोसा करें जब आगे का स्रोत संख्या के लिए असंभव हो (स्रोत-0 या एक पॉइंटर-एंगल गेटर 0x0a/0e/12/70/71/72) और −18/−16 स्थिति मान्य और गैर-शून्य हो (गैर-शून्य गार्ड relX=0 वाले समूह-चाइल्ड अंकों को छोड़ देता है)।
  • 0x17 = तिथि (महीने का दिन), 0x24 = तापमान — अलग। डायल 340 दोनों का उपयोग करता है (0x17 "Jun 09" और एक अलग 0x24 तापमान), इसलिए 0x17 तिथि है, तापमान नहीं। एक डायल जिसकी घड़ी 0x17 स्लॉट में तापमान दिखाती है, वह उपयोगकर्ता-कॉन्फ़िगर्ड कॉम्प्लिकेशन (डिवाइस स्थिति) है, फ़ाइल डिफ़ॉल्ट नहीं।
  • decकार्डdecकार्ड
    0खाली स्लॉट49–53,97–98मौसम
    1–6कदम54–58टाइमर
    7–12कैलोरी59–62श्वास
    13–18स्टैंड63,65–67स्टॉपवॉच
    19–24मध्यम गतिविधि68–71बैटरी
    25–30हृदय गति72–76हाल ही में
    31–36SpO₂77–81संपर्क
    37–42तनाव82–86डायल / फोन
    43–48नींद87–91स्पोर्ट (sportId ≠ 0)
    92संगीत93/94/96गतिविधि रिकॉर्ड / PAI / चक्र