# PUCKS Plugin Teknik Spesifikasyonu **Spec Versiyonu: 1.1.0** · Son güncelleme: 2026-06-24 --- ## 0. Güvenlik ve Sandbox Modeli (ÖNCE BUNU OKU) > **Pluginler KOD değil, BİLDİRİMSEL VERİDİR.** Bir plugin, cihazda kendi kodunu > **çalıştıramaz**. Plugin yalnızca üç şey içerir: bir `manifest.json` (kimlik + > davranış bayrakları), opsiyonel bir `config_schema.json` (kullanıcı ayarı alanları) > ve cihazın ürettiği `settings.json` (anahtar/değer string'ler). Çizimi **firmware'e > gömülü hazır render motoru** yapar; plugin sadece onu yapılandırır. Bu sayede plugin > tasarımı gereği güvenlik açığı yaratamaz: rastgele bellek erişimi, ağ çağrısı, dosya > okuma/yazma, kabuk komutu YOKTUR. **Cihaz tarafı zorunlu doğrulamalar (firmware reddeder):** | Kural | Neden | |---|---| | `id` yalnızca `[a-z0-9_-]`, **en fazla 40 karakter** | Path traversal / enjeksiyon engeli | | `id` içinde `/`, `\`, `..`, boşluk, nokta **YASAK** | Dosya yolu kaçışı engeli | | Klasör adı == `manifest.id` | Sahtecilik/karışıklık engeli | | `schema_version` tam olarak `1` | İleride uyumsuz şema reddi | | `target_resolution` tam olarak `"240x240"` | Taşma/bellek güvenliği | | `manifest.json` ve `config_schema.json` **geçerli JSON** olmalı | Bozuk veri reddi | | Dosya boyutu limitleri (manifest/schema/önizleme) aşılırsa **red** | Flash/heap koruması | | Max **8 yüklü**, max **3 aktif** plugin | Kaynak (flash/heap) sınırı | | `settings` değerleri **string** olarak saklanır, tip dönüşümü plugin'de | Bellek-güvenli okuma | **Yapamayacakları (tasarım gereği imkânsız):** kod/script çalıştırma, internet erişimi, başka pluginin/sistemin dosyalarına erişim, partition/firmware değişimi, ekran dışına çizim, sınırsız bellek ayırma. > Bu sınırlardan herhangi birini gevşetmek **kabul edilmez** — yeni bir çizim > davranışı gerekiyorsa firmware'e built-in render eklenir veya dinamik çizim motoru > (custom clock şema motoru gibi) güvenli biçimde genişletilir; plugin formatı kod > yürütmeye AÇILMAZ. --- ## 1. Plugin Klasör Yapısı ``` myplugin/ ├── manifest.json ★ zorunlu ├── config_schema.json ○ opsiyonel — kullanıcı ayarları ├── preview.png ○ (sadece app) └── screenshots/ ○ galeri (sadece app) ├── 01.png └── 02.png ``` --- ## 2. manifest.json ```jsonc { "id": "my_plugin", "name": "Plugin Adı", "description": "Açıklama", "version": "1.0.0", // ★ ZORUNLU "author": "Yazar", "schema_version": 1, "target_resolution": "240x240", "display_mode": "both", "remote_control": { "tap_cycle": [ { "label": "Aç" } ] } } ``` | Alan | Kural | |---|---| | `version` | **Zorunlu**, boş olamaz | | `schema_version` | Tam olarak `1` | | `target_resolution` | Tam olarak `"240x240"` | | `display_mode` | `"content_only"` / `"menu_only"` / `"both"` (bkz. §7) | | `remote_control` | Opsiyonel. Android Uzaktan Kontrol ekranındaki çoklu tıklama davranışını tanımlar (bkz. §7.4) | ### 2.1 Opsiyonel Kalite Metaverisi (markette daha iyi görünüm) Aşağıdaki alanlar zorunlu değildir ama eklendiğinde plugin markette daha kaliteli görünür ve kullanıcı doğru cihaz/sürümde kurar: | Alan | Tür | Açıklama | |---|---|---| | `category` | string | `widget` / `clock` / `fun` / `info` / `other` — markette gruplama | | `tags` | string[] | Arama/filtre etiketleri (ör. `["takvim","verimlilik"]`) | | `author_url` | string | Yazar/proje bağlantısı (yalnız gösterim; tıklama onaylı açılır) | | `min_firmware_build` | int | Bu plugin'in çalışması için gereken en düşük firmware build | | `summary` | string | Tek satır kısa özet (liste kartında gösterilir, ≤ 80 karakter) | > Bu alanlar yalnızca **gösterim/uyumluluk** içindir; cihazda davranış/güvenlik > etkisi yoktur. Bilinmeyen ek alanlar yok sayılır (ileri uyumluluk). --- ## 3. config_schema.json — Ayar Alan Tipleri ```json { "fields": [ ...her alan bir obje... ] } ``` ### 3.1 string — Serbest Metin ```json { "key": "mesaj", "type": "string", "label": "Ekrana Yazılacak Mesaj", "default": "Merhaba!" } ``` Android UI: `OutlinedTextField` ESP32: `settings["mesaj"]` --- ### 3.2 color — Renk Seçici ```json { "key": "bg_color", "type": "color", "label": "Arka Plan Rengi", "default": "#0a0f1e" } ``` Android UI: Renkli kare önizleme + Hex giriş kutusu ESP32: `settings["bg_color"]` → `"#0a0f1e"` string olarak --- ### 3.3 int — Sayı Seçici (Stepper) ```json { "key": "brightness", "type": "int", "label": "Parlaklık", "default": "128", "min": 0, "max": 255, "step": 1 } ``` Android UI: **−** değer **+** butonları ESP32: `settings["brightness"].toInt()` --- ### 3.4 slider — Sürgü (Float/Int) ```json { "key": "hiz", "type": "slider", "label": "Animasyon Hızı", "default": "1.0", "min": 0.1, "max": 5.0, "step": 0.1 } ``` Android UI: Sürgü çubuğu (min/mevcut/max göstergeli) ESP32: `settings["hiz"].toFloat()` *`step < 1` ise ondalıklı gösterir, `step ≥ 1` ise tam sayı.* --- ### 3.5 bool — Anahtar (Switch) ```json { "key": "start_monday", "type": "bool", "label": "Hafta Pazartesi Başlasın", "default": "true" } ``` Android UI: Yeşil/gri Switch ESP32: `settings["start_monday"] == "true"` --- ### 3.6 select — Açılır Liste (Dropdown) ```json { "key": "view_mode", "type": "select", "label": "Varsayılan Görünüm", "default": "monthly", "options": ["monthly", "weekly", "3-day"] } ``` Android UI: Açılır menü — seçili öğe ✓ ile işaretli ESP32: `settings["view_mode"]` → seçilen `"monthly"` / `"weekly"` / `"3-day"` --- ### 3.7 Tüm Ayarlar Bir Arada (Örnek) ```json { "fields": [ { "key": "view_mode", "type": "select", "label": "Görünüm", "default": "monthly", "options": ["monthly", "weekly", "3-day"] }, { "key": "bg_color", "type": "color", "label": "Arka Plan", "default": "#0a0f1e" }, { "key": "theme_color", "type": "color", "label": "Vurgu Rengi","default": "#00bcd4" }, { "key": "text_color", "type": "color", "label": "Metin", "default": "#dce6f5" }, { "key": "brightness", "type": "int", "label": "Parlaklık", "default": "200", "min": 0, "max": 255, "step": 5 }, { "key": "speed", "type": "slider", "label": "Animasyon Hızı","default":"1.0", "min": 0.1, "max": 5.0, "step": 0.1 }, { "key": "start_monday", "type": "bool", "label": "Pazartesi Başlasın","default":"true" }, { "key": "footer_text", "type": "string", "label": "Alt Yazı", "default": "" } ] } ``` --- ## 4. Android Plugin Detay Ekranı | Bölüm | İçerik | |---|---| | **Galeri** | `screenshots/` — HorizontalPager | | **Aktivasyon** | Toggle · max 3 aktif | | **Hakkında** | Ad, sürüm, yazar, açıklama | | **Ayarlar** | Her `field.type`'a göre farklı UI widget | | **Sil** | Onay dialog'u → ESP32 + yerel temizlik | ### Ayar Güncelleme Akışı 1. Kullanıcı widget'ı değiştirir → `updateLocalSetting()` → StateFlow emit → **UI anında güncellenir** 2. 600 ms sonra otomatik kayıt (debounced auto-save) → `POST /save_plugin_settings` 3. ESP32'nin plugin draw fonksiyonu her çağrıda `settings.json` okur → **bir sonraki çizimde yeni değer görünür** 4. Manuel "Kaydet" butonu → anlık kayıt + "Ayarlar kaydedildi" toast ### Ayar UI Widget Tablosu | `type` | Widget | Özellikler | |---|---|---| | `string` | OutlinedTextField | Serbest metin | | `color` | Renk kare + TextField | Gerçek zamanlı renk önizleme | | `int` | − Değer + düğmeleri | `min`, `max`, `step` sınırlı | | `slider` | Slider | `min`/`max`/`step`, ondalık desteği | | `bool` | Switch (yeşil/gri) | `"true"` / `"false"` string | | `select` | ExposedDropdownMenu | Seçili öğe ✓ ile işaretli | --- ## 5. ESP32 Ayar Okuma ```cpp // settings.json'dan okuma (plugin draw fonksiyonunda) String mode = pluginManager.getSetting(pluginId, "view_mode"); // "monthly" String bgHex = pluginManager.getSetting(pluginId, "bg_color"); // "#0a0f1e" int bright = pluginManager.getSetting(pluginId, "brightness").toInt(); float speed = pluginManager.getSetting(pluginId, "speed").toFloat(); bool monday = pluginManager.getSetting(pluginId, "start_monday") == "true"; ``` *Tüm ayarlar string olarak saklanır; plugin kendi tipine dönüştürür.* --- ## 6. LittleFS Yapısı (ESP32) ``` /plugins// ├── manifest.json ├── config_schema.json └── settings.json ← kayıtta oluşur ``` Kısıtlar: Max **3 aktif**, max **8 yüklü**, `MAX_ACTIVE_PLUGINS` / `MAX_PLUGINS` --- ## 7. İçerik Döngüsü ve display_mode ESP32 iki farklı ekran döngüsüne sahiptir: ### 7.1 Normal Döngü (Kısa Basış ile Geçiş) ``` Dashboard → Saat → [aktif modlar] → [aktif pluginler] → GIF → Medya → İstatistik → QR ``` Bir plugin **normal döngüye** dahil edilmek istiyorsa `display_mode` şu değerlerden biri olmalıdır: | `display_mode` | Normal Döngü | Ana Menü | Açıklama | |---|---|---|---| | `"content_only"` | ✅ | ❌ | Yalnızca döngüde görünür | | `"menu_only"` | ❌ | ✅ | Yalnızca menüden erişilir | | `"both"` | ✅ | ✅ | Hem döngü hem menü | > ⚠️ `bildirim` ve `muzik` pluginleri özel sistem modlarıdır; döngüye dahil **edilmez**. ### 7.2 Rutin Modu (Otomatik Geçiş) Rutin modu, cihaz boştayken seçili ekranlar arasında otomatik geçiş yapar. Android uygulamasından **"Döngüdeki Ekranlar"** listesinde hangi ekranların gösterileceği seçilir. Custom pluginler bu listede `plugin_` formatında temsil edilir: | Ekran ID'si | Açıklama | |---|---| | `clock` | Saat | | `weather` | Hava Durumu | | `currency` | Döviz | | `gif` | GIF | | `plugin_takvim` | Custom Plugin: Takvim | | `plugin_ekran_notu` | Custom Plugin: Ekran Notu | **Kurallar:** - Yalnızca `display_mode` değeri `"content_only"` veya `"both"` olan ve **aktif** olan pluginler rutin döngüsüne eklenebilir. - `"menu_only"` pluginler rutin listesinde **görünmez**. - Bir plugin deaktif edilirse, rutin döngüsünde o ekran **atlanır** (hata vermez). ### 7.3 manifest.json'da display_mode Örneği Bir custom plugin'in hem normal döngüde hem de rutin modunda görünmesini istiyorsanız: ```json { "id": "my_widget", "name": "Benim Widget", "version": "1.0.0", "display_mode": "content_only" } ``` Bu plugin aktif edildiğinde: 1. Normal döngüde kısa basışla erişilebilir. 2. Rutin Modu → Döngüdeki Ekranlar listesinde `plugin_my_widget` olarak seçilebilir. Sadece menüden erişilmesini istiyorsanız: `"display_mode": "menu_only"` ### 7.4 Uzaktan Kontrol Çoklu Tıklama (`remote_control.tap_cycle`) Android uygulamasındaki **Uzaktan Kontrol → Ekranlar** alanında bir plugin kartına art arda dokunulduğunda farklı adımlar çalıştırılabilir. Bu davranış `manifest.json` içindeki opsiyonel `remote_control.tap_cycle` dizisiyle tanımlanır: ```json { "id": "takvim", "name": "Takvim", "version": "1.0.0", "display_mode": "both", "remote_control": { "tap_cycle": [ { "label": "Aç" }, { "label": "Haftalık", "settings": { "view_mode": "weekly" } }, { "label": "3 Günlük", "settings": { "view_mode": "3-day" } } ] } } ``` Her `tap_cycle` adımı şu alanları destekler: | Alan | Tür | Açıklama | |---|---|---| | `label` | string | Android uygulamasında "Sonraki: ..." ipucu olarak gösterilir | | `settings` | object | Bu tıklamada `settings.json` içine yazılacak anahtar/değerler | | `open` | bool | Varsayılan `true`. `false` ise ayarlar uygulanır ama plugin ekranı açılmaz | Kurallar: - `tap_cycle` sırası Android tarafında korunur ve tekrar eden tıklamalarda döngüsel çalışır. - `settings` içindeki tüm değerler ESP32 tarafında string olarak saklanır; plugin kendi draw kodunda uygun tipe çevirmelidir. - `open` alanı verilmezse ilgili adım sonunda plugin ekranı açılır. - `remote_control` özeti `/list_screen_plugins` cevabına eklenir; Android burada yalnızca `label` bilgisini kullanır. - Bu özellik yalnızca **aktif** ve ekranı gösterilebilen plugin kartları için anlamlıdır. --- ## 8. REST API | Endpoint | Metod | | |---|---|---| | `/list_screen_plugins` | GET | Liste | | `/upload_screen_plugin?id=` | POST | manifest.json | | `/upload_screen_plugin_schema?id=` | POST | config_schema.json | | `/activate_screen_plugin?id=` | GET | Toggle (aç/kapat) | | `/api/ensure_screen_plugin_active` | POST | Kapatmadan aktif tutar — ayar ekranlarında bunu tercih et | | `/delete_screen_plugin?id=` | GET | Sil | | `/get_plugin_settings?id=` | GET | Mevcut ayarlar | | `/save_plugin_settings?id=` | POST JSON | Kaydet | | `/api/stats` | GET | `fs_free`, heap | --- ## 9. Plugin Listesi Ekranı (Android) | Eylem | | |---|---| | Karta dokunuş | Detay ekranı | | Uzun basış | Kırmızı sil overlay | | ↻ Yenile | ESP32'den liste + versiyon karşılaştırma | --- ## 10. Sistem Değişmezleri | Kural | | |---|---| | 1.2 s → Menü | Her ekranda | | 10 s → Restart | Her ekranda | | Max 3 aktif | ESP32 reddeder | | Görseller | Sadece Android | | `version` zorunlu | Boş olamaz | --- ## 11. Kalite ve En İyi Uygulamalar Daha kaliteli, cihaz-dostu pluginler için: **Tasarım (240×240):** - Güvenli kenar boşluğu **8–12 px**; metin/şekiller ekrandan taşmamalı. - Yüksek kontrast renk kullan (koyu zemin + parlak vurgu okunur kalır). - Tek/çift haneli sayı, uzun metin ve farklı dillerde taşmayı test et. - Renkleri kullanıcıya `color` alanıyla bırak; sabit renk gömme. **Ayar tasarımı (`config_schema.json`):** - `key` adları `[a-z0-9_]`, kısa ve anlamlı olsun; her alana net `label` ver. - Sayısal alanlarda **mutlaka** `min`/`max` ver — uç değerler ekranı bozmasın. - Mantıklı `default` ver; plugin ilk kurulumda ayarsız da düzgün görünmeli. - Az sayıda, gerçekten gerekli ayar koy (kalabalık ayar = kötü deneyim). **Davranış:** - `display_mode`'u doğru seç: sürekli izlenecek içerik `content_only`/`both`, yalnız ara sıra açılan araç `menu_only`. - `summary` ve `screenshots/` ekleyerek markette güven ver. - Sürümü her değişiklikte artır (`version`); kullanıcı güncellemeyi görsün. **Performans (cihaz kısıtları):** - Cihaz her çizimde `settings.json` okur; ayarları sade tut. - Render motoru 240×240 sprite buffer kullanır — taşan koordinat verme. - `min_firmware_build` ile yeni render özelliklerine bağımlılığı işaretle. --- ## Değişiklik Geçmişi | Versiyon | Değişiklik | |---|---| | **1.1.0** | **Güvenlik ve Sandbox modeli** (§0) belgelendi: pluginler bildirimsel veridir, kod çalıştırmaz; cihaz-tarafı zorunlu doğrulamalar ve limitler listelendi. **Opsiyonel kalite metaverisi** (§2.1: category, tags, summary, author_url, min_firmware_build). **Kalite ve En İyi Uygulamalar** bölümü (§11). Marka adı PUCKS. | | **1.0.6** | `remote_control.tap_cycle` eklendi. Custom pluginler artık Android Uzaktan Kontrol ekranında çoklu tıklama adımları, etiketleri ve opsiyonel ayar yazımı tanımlayabiliyor. | | **1.0.5** | `display_mode` alanının İçerik Döngüsü ve Rutin Moduna etkisi belgelendi. Custom pluginlerin Rutin Modu döngüsüne `plugin_` formatıyla dahil edilmesi açıklandı. | | 1.0.4 | Debounced auto-save (600 ms): kullanıcı değişiklik yaptıkça ESP32'ye otomatik gönderilir. StateFlow anında güncellenerek UI tüm widget tiplerinde (bool/int/slider/select) gerçek zamanlı değişikliği yansıtır. | | 1.0.3 | Zengin ayar alanı sistemi: `select` (dropdown), `slider` (float sürgü), `color` (renk swatch+hex), `int` (stepper +/-), `bool` (switch), `string`. `options`, `min`, `max`, `step` JSON alanları. | | 1.0.2 | `version` zorunlu, uzun basış silme, yenile butonu | | 1.0.1 | `config_schema.json` alan tipleri kılavuzu, `screenshots/` akışı | | 1.0.0 | İlk versiyonlu yayın |