
स्व-होस्टेड नियंत्रण डेक और टेलस्केल के माध्यम से लिनक्स वर्कस्टेशन के लिए रिमोट डेस्कटॉप, जिसमें वेबआरटीसी स्ट्रीमिंग, स्थानीय LLM के साथ वॉइस कंट्रोल, सीन मैक्रोज़, वर्चुअल इनपुट, फ़ाइल ट्रांसफ़र और लाइव सिस्टम टेलीमेट्री शामिल हैं।
# phone-deck
एक अतिरिक्त एंड्रॉइड फ़ोन (या ब्राउज़र वाला कोई भी उपकरण) को अपने लिनक्स वर्कस्टेशन के लिए एक स्व-होस्टेड कंट्रोल डेक और रिमोट डेस्कटॉप में बदलें — आपके निजी टेलस्केल नेटवर्क पर, बिना किसी तीसरे पक्ष के ऐप के।
इसकी शुरुआत "पुराने फ़ोन का क्या करूँ?" से हुई और यह एक हाइपरलैंड रिग के लिए एक नियंत्रण प्लेन बन गया: विंडो/वर्कस्पेस नियंत्रण, दृश्य मैक्रो, स्थानीय-एलएलएम सहायक के साथ एक पुश-टू-टॉक वॉइस रूटर, संदर्भ-जागरूक इन-ऐप नियंत्रण, दो-तरफ़ा ऑडियो + स्क्रीन स्ट्रीमिंग, एक टच रिमोट-डेस्कटॉप, एक वर्चुअल कीबोर्ड/माउस, फ़ाइल स्थानांतरण + फ़ोन→PC साझाकरण, और लाइव टेलीमेट्री — सभी एक इंस्टॉल किए गए PWA के रूप में एक फ़ॉस्फ़र-टर्मिनल लुक के साथ परोसा जाता है।
> **पोर्टेबिलिटी पर ध्यान दें।** यह एक विशिष्ट सेटअप के लिए बनाया गया था: **Arch-आधारित लिनक्स (Garuda) + Hyprland + PipeWire + NVIDIA + Tailscale**, [end-4 / illogical-impulse](https://github.com/end-4/dots-hyprland) डॉटफ़ाइल्स के साथ। आर्किटेक्चर सामान्य है, लेकिन कई एकीकरण पर्यावरण-विशिष्ट हैं (मॉनिटर नाम, वर्कस्पेस बाइंडिंग, matugen थीम पथ, `cpupower`/`nvidia-smi`)। इसे एक तैयार पैकेज के बजाय अनुकूलित करने के लिए कार्यशील संदर्भ मानें।
---
## विशेषताएँ
यूआई एक टैब्ड, डार्क, लैंडस्केप वेब ऐप है (आपके डेस्कटॉप से थीम-सिंक):
| टैब | यह क्या करता है |
|---|---|
| **वर्कस्पेस** | लाइव प्रति-मॉनिटर वर्कस्पेस ग्रिड (स्विच करने के लिए टैप करें), लाइव विंडो सूची (फ़ोकस करने के लिए टैप करें), प्रति-मॉनिटर DPMS टॉगल + `ddcutil` ब्राइटनेस |
| **रिमोट** | ऑन-स्क्रीन ट्रैकपैड (ड्रैग/टैप/दो उंगली) + मॉडिफ़ायर और कॉम्बो के साथ वर्चुअल कीबोर्ड, एक कर्नेल `uinput` डिवाइस के माध्यम से |
| **मोड** | एक-टैप दृश्य मैक्रो — पूरे ऐप लेआउट को मॉनिटरों पर लॉन्च/बंद/व्यवस्थित करें (जैसे "काम" / "खाली") |
| **वॉइस** | पुश-टू-टॉक वॉइस रूटर (faster-whisper, CPU): लीड-वर्ड लेन — `macro` (एक मोड/कमांड चलाएँ), `type` (डिक्टेट), `input` (वाक्यांश → की कॉर्ड) — **निष्पादित करने से पहले पुष्टि** के साथ, साथ ही **"friday"**, एक केवल-पढ़ने के लिए स्थानीय-एलएलएम उत्तरदाता (Ollama + Qwen) जिसमें स्व-होस्टेड वेब खोज और लाइव सिस्टम-स्थिति जागरूकता है |
| **ऑडियो** | माइक/स्पीकर म्यूट, वॉल्यूम, आउटपुट **और** इनपुट डिवाइस पिकर, `playerctl` ट्रांसपोर्ट + कवर आर्ट (सक्रिय MPRIS प्लेयर को ट्रैक करता है) |
| **स्ट्रीम** | द्विदिश **WebRTC ऑडियो** (PC↔फ़ोन, फ़ोन-एक-माइक और फ़ोन-केवल आउटपुट के साथ) **+ स्क्रीन वीडियो**, और **स्ट्रीम की गई स्क्रीन पर नियंत्रण करने के लिए टैप करें** = एक वास्तविक रिमोट डेस्कटॉप |
| **सिस्टम** | प्रदर्शन-मोड टॉगल, लाइव **थीम** स्विचिंग, Tailscale स्थिति, शीर्ष प्रक्रियाएँ (मारने के लिए टैप करें), लॉक, NetworkManager पुनरारंभ, सस्पेंड/रीबूट/पावरऑफ़ |
| **फ़ाइलें** | मॉनिटर का स्क्रीनशॉट लें → फ़ोन पर देखें/डाउनलोड करें; फ़ोन→PC पर एक फ़ाइल ड्रॉप करें |
| **कॉन्फ़िग** | फ़ोन से `commands.json` / `modes.json` (और संदर्भ/वॉइस) कॉन्फ़िग संपादित करें (JSON-मान्य) |
हमेशा ऊपर, टैब के ऊपर — एक **संदर्भ पट्टी** जो अभी जो हो रहा है उसे सामने लाती है: एक सक्रिय कॉल (म्यूट करें/उस पर जाएँ), मीडिया अब-चल रहा है + ट्रांसपोर्ट, और फ़ोकस किए गए ऐप (YouTube / Brave / Teams) के लिए इन-ऐप कीबोर्ड नियंत्रण, *फ़ोकस चुराए बिना* वितरित।
दो और, टैब से परे:
- **रिग पर भेजें** — PWA एंड्रॉइड **शेयर लक्ष्य** के रूप में पंजीकृत होता है: अपने फ़ोन के किसी भी ऐप से एक छवि, कुछ टेक्स्ट, या एक लिंक साझा करें और यह रिग पर पहुँचता है — छवियाँ एक ड्रॉप निर्देशिका में सहेजी जाती हैं *और* क्लिपबोर्ड पर पेस्ट-तैयार PNG के रूप में रखी जाती हैं, टेक्स्ट क्लिपबोर्ड पर, एक नंगा लिंक खोला जाता है।
- **परिवेश विचारक** — कुछ निष्क्रिय मिनटों के बाद डेक एक फ़ॉस्फ़र उपकरण पैनल बन जाता है: पीक-होल्ड के साथ सुई-डायल टेलीमेट्री ("क्या यह दूर रहने पर पीक पर था?"), घड़ी, अब-चल रहा है।
प्लस: ऑन-स्क्रीन नम्पैड PIN लॉगिन, स्क्रीन वेक-लॉक, फ़ुलस्क्रीन लैंडस्केप PWA, और लॉगिन पर ब्रूट-फोर्स लॉकआउट।
---
## आर्किटेक्चर
```
फ़ोन / लैपटॉप (ब्राउज़र PWA)
│ HTTPS + WSS (केवल Tailscale)
▼
tailscale serve ──► FastAPI वेब ऐप ── यूनिक्स सॉकेट ──► deckd
(वास्तविक TLS प्रमाणपत्र) (आपके उपयोगकर्ता के रूप में चलता है) (कार्य नाम) (रूट के रूप में चलता है)
│ │
hyprctl · pactl · ddcutil · grim · निश्चित अनुमति सूची
wf-recorder · uinput · WebRTC विशेषाधिकार प्राप्त कमांड
(cpupower, nvidia-smi,
systemctl, …)
```
दो प्रक्रियाएँ:
- **`app/` — वेब ऐप** आपके सामान्य उपयोगकर्ता के रूप में चलता है। यह वह सब कुछ करता है जिसके लिए रूट की आवश्यकता नहीं है: Hyprland नियंत्रण, ऑडियो, चमक, स्क्रीन कैप्चर, वर्चुअल इनपुट, WebRTC स्ट्रीमिंग, फ़ाइल स्थानांतरण।
- **`deckd/` — एक छोटा रूट सहायक** (केवल stdlib, कोई निर्भरता नहीं) कुछ विशेषाधिकार प्राप्त कार्यों के लिए। वेब ऐप इसे कभी शेल स्ट्रिंग नहीं भेजता — केवल एक निश्चित अनुमति सूची से **कार्य नाम** (`governor_performance`, `gpu_power_limit`, `suspend`, …), जो किसी भी चीज़ को निष्पादित करने से पहले [`deckd/actions.py`](https://github.com/smit-shah-gg/phone-deck/blob/main/deckd/actions.py) में मान्य किया जाता है। यहाँ तक कि अगर वेब ऐप पूरी तरह से समझौता कर लिया गया हो, तो विस्फोट त्रिज्या ठीक अनुमति सूची वाले कार्यों तक सीमित है, जिसमें कोई तर्क इंजेक्शन नहीं है। सॉकेट `root:<group>` मोड `0660` है।
### सुरक्षा मॉडल
- **केवल आपके tailnet के अंदर पहुँचा जा सकता है** — `tailscale serve` इसे आपके MagicDNS नाम पर एक वास्तविक Let's Encrypt प्रमाणपत्र के साथ उजागर करता है; यह कभी `0.0.0.0` से बाइंड नहीं होता है।
- **JWT** लॉगिन (PIN → हस्ताक्षरित कुकी), बार-बार विफलता के बाद एक्सपोनेंशियल-बैकऑफ़ लॉकआउट के साथ।
- विशेषाधिकार प्राप्त कार्य अनुमति सूची वाले सहायक डेमॉन के पीछे पृथक होते हैं।
- अपलोड बेसनाम-सैनिटाइज़ किए जाते हैं (ड्रॉप निर्देशिका से बच नहीं सकते)।
> वेब ऐप *आपके* कॉन्फ़िगर किए गए शेल कमांड चला सकता है और आपके उपयोगकर्ता के रूप में इनपुट इंजेक्ट कर सकता है — यह, डिज़ाइन के अनुसार, आपकी मशीन के लिए एक रिमोट कंट्रोल है। इसे अपने tailnet पर, PIN के पीछे रखें, और इसे सार्वजनिक रूप से उजागर न करें।
---
## आवश्यकताएँ
- **Hyprland** (wlroots), **PipeWire** (`pactl`/PulseAudio संगतता के साथ) के साथ लिनक्स
- **Python ≥ 3.11** और **[uv](https://docs.astral.sh/uv/)**
- **Tailscale** (आपके tailnet के लिए HTTPS प्रमाणपत्र सक्षम)
- विभिन्न सुविधाओं द्वारा उपयोग किए जाने वाले CLI उपकरण (आप जो उपयोग करना चाहते हैं उसे स्थापित करें):
`hyprctl`, `pactl` / `pw-record` / `pw-play`, `playerctl`, `ddcutil`,
`wf-recorder`, `grim`, `cpupower`, `nvidia-smi`, `kitty` (या आपका टर्मिनल)
- आपका उपयोगकर्ता **`input`** समूह में (`/dev/uinput` के लिए) और **`i2c`** समूह में (`ddcutil` के लिए)
- ऑडियो/वीडियो स्ट्रीमिंग के लिए `aiortc` + PyAV (`uv` के माध्यम से स्थापित)
---
## स्थापना
```bash
git clone <आपका-रेपो-यूआरएल> phone-deck
cd phone-deck
uv sync # .venv बनाता है और निर्भरताएँ स्थापित करता है
uv run python -m app.set_pin # अपना अनलॉक PIN सेट करें
```
### 1. रूट सहायक (`deckd`)
पहले `systemd/deckd.service` संपादित करें — पथ और `DECK_SOCKET_GROUP` को एक समूह में सेट करें जिससे आपका उपयोगकर्ता संबंधित है (आमतौर पर आपका प्राथमिक समूह):
```bash
sudo cp systemd/deckd.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now deckd
```
`deckd` सिस्टम Python चलाता है (केवल stdlib — कोई venv आवश्यक नहीं)।
### 2. वेब ऐप
विकास के लिए इसे सीधे चलाएँ:
```bash
uv run uvicorn app.main:app --host 127.0.0.1 --port 8765
```
…या उपयोगकर्ता सेवा के रूप में। पहले `systemd/phone-deck.service` पथ संपादित करें, फिर इसे स्थापित करें।
**महत्वपूर्ण:** एक सेटअप पर जहाँ Hyprland *uwsm* के माध्यम से लॉन्च नहीं किया जाता है, systemd `graphical-session.target` कभी सक्रिय नहीं होता है, इसलिए उपयोगकर्ता यूनिट लॉगिन पर स्वचालित रूप से प्रारंभ नहीं होगी। विश्वसनीय उपाय यह है कि इसे सत्र वातावरण आयात करते हुए Hyprland से प्रारंभ किया जाए — अपने Hyprland ऑटोस्टार्ट में जोड़ें (जैसे end-4 का `~/.config/hypr/custom/execs.conf`):
```bash
exec-once = systemctl --user import-environment WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_RUNTIME_DIR DBUS_SESSION_BUS_ADDRESS XDG_CURRENT_DESKTOP && systemctl --user start phone-deck
```
### 3. Tailscale पर HTTPS
1. प्रमाणपत्र एक बार सक्षम करें: Tailscale एडमिन कंसोल → **DNS → Enable HTTPS Certificates**.
2. ऐप को वास्तविक प्रमाणपत्र के साथ फ़्रंट करें:
```bash
sudo tailscale serve --bg 127.0.0.1:8765
```
3. HTTPS लाइव होने के बाद वेब-ऐप वातावरण में `DECK_SECURE_COOKIES=1` सेट करें।
### 4. फ़ोन पर स्थापित करें
फ़ोन पर Chrome में `https://<आपका-होस्ट>.<आपका-tailnet>.ts.net/` खोलें (यह आपके tailnet पर होना चाहिए), लॉग इन करें, फिर **⋮ → Install app**। यह फ़ुलस्क्रीन, लैंडस्केप-लॉक, स्क्रीन को जागृत रखते हुए लॉन्च होता है। PWA सेवा वर्कर + वेक-लॉक के लिए HTTPS आवश्यक है।
> किसी भी tailnet डिवाइस से काम करता है — लैपटॉप भी शामिल हैं (टच सतहें Pointer Events का उपयोग करती हैं, इसलिए माउस भी काम करता है)। WebRTC स्ट्रीम एकल-पीयर है (एक समय में एक दर्शक)।
---
## विन्यास
सभी रनटाइम कॉन्फ़िग `~/.config/phone-deck/` में रहता है और **Config** टैब (JSON-सत्यापित, एक `.bak` रखता है) से संपादन योग्य है। परिवर्तन अगली कार्रवाई पर प्रभावी होते हैं — कोई पुनरारंभ नहीं।
### `commands.json` — कस्टम शेल क्रियाएँ
```json
[
{ "id": "reload_wm", "label": "WM पुनः लोड करें", "run": "hyprctl reload" },
{ "id": "deploy", "label": "साइट परिनियोजित करें", "run": "cd /srv/app && ./deploy.sh",
"confirm": true, "timeout": 120 }
]
```
`run` एक स्ट्रिंग (`bash -lc` के माध्यम से निष्पादित) या एक सूची (argv, कोई शेल नहीं) हो सकता है। ये आपके उपयोगकर्ता के रूप में चलते हैं — यह एक स्थानीय, विश्वसनीय फ़ाइल है।
### `modes.json` — दृश्य मैक्रो
प्रत्येक मोड `hyprctl` के माध्यम से चलाए जाने वाले चरणों की एक क्रमबद्ध सूची है:
- `launch {cmd, workspace, match?, once?}` — एक वर्कस्पेस पर एक ऐप खोलें। `match` (विंडो क्लास) + `once` के साथ, यह डुप्लिकेट नहीं होगा, और यदि विंडो गलत वर्कस्पेस पर आती है तो उसे स्थानांतरित करता है (ब्राउज़र PWA जैसे ऐप्स के लिए आवश्यक है जिनकी विंडो मौजूदा प्रक्रिया से आती है)।
- `close {match, workspaces?}` — एक क्लास की विंडो बंद करें, वैकल्पिक रूप से केवल सूचीबद्ध वर्कस्पेस पर।
- `move {match, workspace}` — मिलान करने वाली विंडो को एक वर्कस्पेस पर ले जाएँ।
- `focus {workspace}` — एक वर्कस्पेस को दृश्य में लाएँ (उदा. प्रत्येक मॉनिटर को उसके होम ws पर रीसेट करें)।
### Hyprland वर्कस्पेस → मॉनिटर बाइंडिंग
मोड प्लेसमेंट केवल तभी नियतात्मक है जब आपके वर्कस्पेस मॉनिटर से पिन किए गए हों। अपने Hyprland कॉन्फ़िग में बाइंडिंग जोड़ें (अपने अनुसार वर्कस्पेस नंबर और मॉनिटर नाम समायोजित करें):
```
workspace = 1, monitor:DP-3, default:true
workspace = 2, monitor:DP-3
# … आदि
```
### थीम
डेक एक **फ़ॉस्फ़र-टर्मिनल** त्वचा पहनता है जिसमें चार लाइव-स्विच करने योग्य रंग प्रोफ़ाइल हैं — **हरा** (डिफ़ॉल्ट), **एम्बर**, **बर्फ**, और **ऑटो** (आपके matugen वॉलपेपर पैलेट से फ़ॉस्फ़र रंग प्राप्त करता है, `~/.local/state/quickshell/user/generated/colors.json`)। इसे **System** टैब में स्विच करें; चयन `~/.config/phone-deck/theme.json` में बना रहता है। `app/theme.py` इसे `/theme.css` के रूप में परोसता है। परिवेश विचारक का निष्क्रिय समय और संस्कार पंक्ति भी यहाँ कॉन्फ़िगर करने योग्य है।
---
## स्ट्रीमिंग कैसे काम करती है
द्विदिश WebRTC **aiortc + PyAV** के माध्यम से, सबप्रोसेस पाइप (नाजुक डिवाइस-I/O बाइंडिंग से बचता है) के साथ PipeWire/Hyprland से जुड़ा:
- **PC → फ़ोन ऑडियो:** `parec` के साथ डिफ़ॉल्ट सिंक के `.monitor` को कैप्चर करें → Opus।
- **फ़ोन → PC माइक:** फ़ोन माइक प्राप्त करें → एक PipeWire नल सिंक जिसके मॉनिटर ऐप इनपुट के रूप में चुनते हैं ("PhoneDeckMic का मॉनिटर")।
- **केवल फ़ोन आउटपुट:** PC प्लेबैक को एक वर्चुअल सिंक में रूट करें ताकि स्पीकर चुप हो जाएँ जब आप फ़ोन पर सुन रहे हों; रोकने पर पुनर्स्थापित किया जाता है।
- **PC → फ़ोन वीडियो:** `wf-recorder` एक मॉनिटर को कैप्चर करता है → aiortc, गुणवत्ता प्रीसेट (540p / 720p / 1080p) के साथ ताकि कमजोर लिंक पर बिटरेट सामान्य रहे।
- **टच रिमोट डेस्कटॉप:** स्ट्रीम किए गए वीडियो पर टैप/ड्रैग → पूर्ण कर्सर स्थिति (`hyprctl movecursor`) + `uinput` क्लिक। टैप = क्लिक, ड्रैग = मूव, लॉन्ग-प्रेस = राइट-क्लिक।
बाधाएँ: ~150–300 ms विलंबता; केवल तभी विश्वसनीय जब PWA अग्रभूमि में हो (एंड्रॉइड पृष्ठभूमि टैब को निलंबित करता है); इको से बचने के लिए हेडफ़ोन का उपयोग करें; एक समय में एक स्ट्रीमिंग पीयर; ब्राउज़र फ़ोन के *अपने* ऐप ऑडियो को कैप्चर नहीं कर सकता (केवल माइक)।
---
## टेक स्टैक
- **बैकएंड:** FastAPI + Uvicorn, WebSockets, aiortc/PyAV, python-evdev, psutil, PyJWT
- **फ्रंटएंड:** HTML + Tailwind (CDN) + वैनिला JS, PWA (मैनिफेस्ट + सेवा कार्यकर्ता + वेक-लॉक)
- **सहायक डेमॉन:** केवल Python मानक पुस्तकालय
- **ट्रांसपोर्ट:** Tailscale (HTTPS/WSS के लिए `tailscale serve`)
## परीक्षण
```bash
uv run pytest
```
`deckd` अनुमति सूची, JWT + लॉगिन लॉकआउट, PIN हैशिंग, कॉन्फ़िग-एडिटर सत्यापन, मोड पार्सिंग, स्ट्रीमिंग गुणवत्ता/कमांड तर्क, और इनपुट कीमैप को कवर करता है। लाइव WebRTC/PipeWire/Hyprland पथ विकास के दौरान लूपबैक द्वारा मान्य किए जाते हैं।
## परियोजना लेआउट
```
app/ FastAPI वेब ऐप
main.py रूट + WebSockets
hypr.py Hyprland (स्नैपशॉट, डिस्पैच, इवेंट सॉकेट, कर्सर)
audio.py PipeWire डिवाइस नियंत्रण + playerctl
audio_rtc.py WebRTC ऑडियो + स्क्रीन वीडियो ब्रिज
hid.py uinput के माध्यम से वर्चुअल कीबोर्ड/माउस
modes.py दृश्य-मैक्रो इंजन
brightness.py ddcutil
grab.py स्क्रीनशॉट + फ़ाइल अपलोड
theme.py matugen → CSS
...
deckd/ रूट सहायक (केवल stdlib) + कार्य अनुमति सूची
systemd/ सेवा इकाइयाँ
tests/ pytest सूट
```
## लाइसेंस
MIT — `LICENSE` देखें (यदि आप एक अलग लाइसेंस चाहते हैं तो प्रकाशित करने से पहले एक जोड़ें)।
## धन्यवाद
[Hyprland](https://hyprland.org/), [PipeWire](https://pipewire.org/),
[Tailscale](https://tailscale.com/), [aiortc](https://github.com/aiortc/aiortc), और
[end-4 डॉटफ़ाइल्स](https://github.com/end-4/dots-hyprland) के लिए बनाया गया।