IS
Barcha tajribalar
jonli

day-summ – kamera, puls va lokal LLM bilan belgilangan kun

Noutbuk kamerasi daqiqalarni fokus, telefon va charchoqqa ajratadi, Whoop bilaguzugi BLE orqali pulsni uzatadi, lokal model esa kechki xulosani yozadi. To'liq oflayn: kadrlar saqlanmaydi, bazada faqat daqiqalik raqamlar.

laboratoriya yozuvi
Nega qildim

Whoop puls, uyqu va recovery'ni ko'rsatadi, lekin o'sha daqiqalarda nima qilganim haqida jim. Ekran vaqti trekerlari esa aksincha: ilovani biladi, ammo umuman ekranga qaraganimni bilmaydi. Fiziologiya va xatti-harakat bitta vaqt o'qida yotadigan yagona kun manzarasini xohladim – va buning uchun hech narsani bulutga bermaslikni.

Nimani o'rgandim

Eng foydali signal kameradan emas, uning yo'qligidan chiqdi. Kadrda yuz yo'q, lekin BLE orqali puls kelib turibdi – demak men yaqindaman, bu qisqa tanaffus. Puls ham yo'qolgan bo'lsa – xonadan chiqib ketganman. Bitta `hr_avg is not None` tekshiruvi butun bir evristikalar sinfini almashtirdi. Yana: MediaPipe blendshape'larni (ko'z pirillashi, esnash, pastga qarash) tayyor raqam sifatida beradi, shuning uchun PERCLOS va pirillash chastotasi bir necha qatorda, model o'qitmasdan hisoblanadi.

Qayerda sindi

Batareya uchun kamera bo'laklab suratga oladi: har 5 daqiqada ~10 soniya. Yigirma daqiqalik yo'qlik esa taymlaynda to'rtta bir daqiqalik dog'ga aylanardi – chunki faqat kadr bo'lgan daqiqalar belgilanardi. Yechim sample-and-hold bo'ldi: har bo'lak o'z kategoriyasini keyingisiga qadar ushlab turadi, lekin cheklov bilan – aks holda yopiq qopqoq butun oqshomga yoyilib ketardi.

Stek
PythonMediaPipeYOLO11Whoop BLEOllamaSQLite

Bitta kun, bitta o'q

Tanam haqidagi ma'lumot allaqachon bor edi: Whoop puls, uyqu va recovery'ni halol yozadi. Ammo u "o'sha payt nima qilayotgan eding" degan savolga javob bermaydi. Ekran vaqti trekerlari esa buning teskarisi: ilovani biladi, lekin ekranga qaraganimni yoki noutbuk yonida telefonga tikilganimni bilmaydi.

day-summ ikkala oqimni bitta kechki hisobotga jamlaydi: daqiqama-daqiqa taymlayn, charchoq oynalari, fokusdagi puls va telefondagi puls – ustiga shu noutbukda ishlaydigan model yozgan matnli xulosa.

jonli demo· sandbox
Demo yuklanmoqda…

Daqiqa o'z belgisini qanday oladi

Kadrlar birma-bir tahlil qilinmaydi: hammasi daqiqalik yozuvga yig'iladi (kadrdagi mavjudlik ulushi, ekranga qarash ulushi, telefonli kadrlar ulushi, pirillashlar, PERCLOS, o'rtacha puls), keyin daqiqa toza funksiya orqali aynan bitta kategoriya oladi:

def minute_category(rec, cfg, workout_spans):
    if not rec.data_ok:
        return "nodata"
    if overlaps_workout(rec, workout_spans):
        return "workout"
    if rec.presence_ratio < cfg.break_presence_max:
        # yuz yo'q: puls kelyapti → yaqinman, puls yo'q → chiqib ketganman
        return "break" if rec.hr_avg is not None else "left"
    if rec.phone_detected_ratio > cfg.phone_minute_ratio:
        return "phone"
    if (rec.presence_ratio >= cfg.focus_presence
            and rec.gaze_on_screen_ratio >= cfg.focus_gaze
            and rec.phone_detected_ratio < cfg.focus_phone_max):
        return "focus"
    if rec.gaze_on_screen_ratio < cfg.away_gaze_max:
        return "away"
    return "idle"

Yuz va qarashni MediaPipe Face Landmarker hisoblaydi: u blendshape'larni (eyeBlinkLeft, jawOpen, eyeLookDown, browDown) va boshning burilish matritsasini beradi, undan pitch va yaw olinadi. Shu yerdan daqiqadagi pirillashlar va PERCLOS – ko'z yumuq o'tgan vaqt ulushi, uyquchanlikning klassik ko'rsatkichi – chiqadi.

Kadrdagi telefonni YOLO11-nano COCO klassi cell phone bo'yicha ushlaydi va faqat har o'ninchi kadrda, chunki detektor yuz modelidan og'irroq.

Tanaffusmi yoki ketganmi – pulsga bog'liq

Loyihaning eng yoqimli topilmasi. Kamera bir xil narsani – bo'sh kadrni – mutlaqo boshqa ikki holatda ko'radi: men kresloga suyanib o'ylayapman yoki xonadan chiqib ketdim. Ularni BLE ajratadi: Broadcast Heart Rate yoqilganda Whoop bilaguzugi standart Heart Rate Service orqali uzatadi va bir necha metr radiusda bo'lsam, puls kelaverdi. Puls yo'qolsa – demak men ketganman, yuz o'girganim emas.

Batareya: uzluksiz suratga olish o'rniga bo'laklar

Kamerani kun bo'yi yoqib qo'yish – qiziyotgan noutbuk va bir necha soat avtonomlikning yo'qolishi. Shuning uchun sukut bo'yicha kamera har 5 daqiqada 10 soniyaga uyg'onadi (konfigda sample_duration_sec / sample_interval_sec), BLE-puls esa doim ulanib turadi – u deyarli energiya yemaydi. macOS'da qopqoq ioreg orqali tekshiriladi (AppleClamshellState): yopiq bo'lsa, kamera umuman qo'zg'atilmaydi.

Matnni model yozadi, raqamlarni kod hisoblaydi

Hisobot tepasidagi xulosani lokal ollama orqali qwen3:8b generatsiya qiladi. Muhim tamoyil: model hisoblamaydi – u faqat so'zlab beradi. Barcha faktlarni (fokus daqiqalari, charchoq oynalari, kategoriyalar bo'yicha puls, Whoop API'dan recovery va uyqu) kod hisoblaydi va tayyor JSON qilib beradi, prompt esa to'g'ridan-to'g'ri "qayta hisoblama" deydi.

Agar ollama ishlamayotgan bo'lsa, hisobot buzilmaydi: f-satrlardan shablon xulosa yig'iladi. qwen3'da mulohaza rejimi ham bor – <think>…</think> bloki regulyar ifoda bilan kesib tashlanadi, hisobotga faqat toza matn boradi.

uv run daysumm calibrate   # bir marta: ish joyingizga moslangan chegaralar
uv run daysumm start       # ertalab: yig'ish + localhost:9683 da jonli dashboard
uv run daysumm report      # kechqurun: HTML-hisobotni yig'ish va ochish

Maxfiylik – va'da emas, arxitektura

Bunday narsani "deyarli maxfiy" qilib bo'lmaydi. Yo kadrlar umuman saqlanmaydi, yo bir kuni yarim yillik o'z yuzing turgan papkani topasan. Shuning uchun loyihada kadr diskka yetib boradigan bironta ham kod yo'li yo'q.