Merge branch 'main' of https://gitea.skink-platy.ts.net/admin/Linux_post_install
.
This commit is contained in:
@@ -26,3 +26,7 @@ config/rclone.conf
|
||||
|
||||
# Stray session capture (accidentally created in the repo root)
|
||||
session
|
||||
|
||||
# Scratch/plan notes — never committed
|
||||
TEMP_PLAN.md
|
||||
dream.md
|
||||
|
||||
@@ -16,6 +16,8 @@ summary (newest last).
|
||||
|
||||
## Done
|
||||
|
||||
- **2026-08-13** — `pos communication scrcpy` (`bin/pos-communication-scrcpy`): wrapper over scrcpy+adb for Android mirroring/control. Subcommands: bare `scrcpy` (mirror — config defaults + verbatim pass-through of any scrcpy flag; no device → friendly error + hints), `devices` (`adb devices -l`), `record [file] [--headless]` (default `$SCRCPY_RECORD_DIR/<device>_<date>.mp4`, `--headless` = `--no-playback` for headless servers), `tcpip [port]` (USB→wireless switch + prints `connect` with the auto-detected device IP), `connect <ip[:port]>` (adb connect + mirror `-s`), `push` (default `/sdcard/Download` = scrcpy's own default), `pull`, `screenshot` (`adb exec-out screencap -p` → PNG in RECORD_DIR), `info` (model/android/sdk/serial via getprop). Config scope `scrcpy` (`~/.config/linux_post_install/scrcpy.env`, `pos config scrcpy`): `SCRCPY_SERIAL/MAX_SIZE/MAX_FPS/BIT_RATE/FULLSCREEN/RECORD_DIR/PUSH_TARGET/EXTRA_FLAGS`, env-var precedence. Deps `scrcpy` + `adb` added to preinstall PACKAGES; docs note the apt build is older and point to the existing `apps/media/scrcpy.sh` app installer (GitHub latest, bundles adb) — researched 2026 releases (current v4.1). Conventions: `# POS:`/`# POS_SUBCMDS:`/`# POS_CONFIG:` headers, deps guards before `-h|--help`, no stdin → no INTERACTIVE_CMDS. Verified: `bash -n`, stub-PATH suite `/tmp/opencode/scrcpy-run-test.sh` 21/21 green (fake adb/scrcpy echo-args, HOME isolation, env/file precedence, rc paths, screenshot bytes), `make gen && make check` green, dispatch via `pos communication scrcpy --help`. Docs: POS.md communication table + detail block, howto/communication.md section, HOWTO.md index + env row, AGENT_Context Common Tasks row + gen'd tree/dispatch/filetable.
|
||||
|
||||
- **2026-08-13** — Entertainment-module hardening (approved Tier 1 + Tier 2): delivery moved to `notify_send` (platform follows `NOTIFY_PLATFORM`, default Telegram) via `lib/notify.sh` sourced by `bin/pos-entertainment-send`; a **last-run state** is recorded per plugin (`~/.local/share/linux_post_install/entertainment/last/<plugin>` — rc + timestamp) on every non-`--print` run and shown by `pos entertainment status`, which also lists installed-but-not-enabled plugins; a send that fails **while fired by a timer** (gated on `$INVOCATION_ID`) additionally notifies the configured platforms. New **message-safe plugin lib** `lib/entertainment-plugin-lib.sh` (defines only `plugin_*`, never writes stdout — the stdout contract stays "message only"): `plugin_load_config` (entertainment.env + env precedence), `plugin_have`, `plugin_require`, `plugin_err`, `plugin_http_json <url> [--key <jq>] [-H <header>]` (curl `--max-time 20 --retry 2`); `weather`/`joke`/`gold` refactored onto it. `pos entertainment config` gains `get|unset|ls|edit` (edit via the shared `pos config` UI — added to `INTERACTIVE_CMDS`). **Tier 2**: new shared lib `lib/user-timers-lib.sh` (only `ut_*`: `ut_interval_to_oncalendar`, `ut_interval_label`, `ut_unit_name`, `ut_write_unit_pair` incl. `TimeoutStopSec=5s` + `Persistent` + network-online deps, `ut_ensure_linger`, `USER_SYSTEMD_DIR`) dedupes the systemd user-timer machinery between `lib/entertainment-lib.sh` and `lib/scheduler-lib.sh` (the latter's `sched_*` duplicates deleted; both source it; collides-with-nothing). `install.sh` Phase 2 lib list += the two new libs; SCRIPTS.md/DEV.md/POS.md/howto/entertainment.md/AGENT_Context updated (hand-maintained lib rows: entertainment-lib 354→311, scheduler-lib 830→760, +112 user-timers-lib, +67 plugin lib). Verified: `bash -n` everywhere; smoke-tested in an isolated `HOME=/tmp/enttest` (status, config get/set/unset/ls, send path rc=0, failing plugin records rc=1, error-case message hygiene); `make gen && make check` green.
|
||||
|
||||
- **2026-08-13** — Fast pos-unit shutdown: every systemd unit a pos tool writes (or `systemd/` ships) now sets `TimeoutStopSec=5s` (+ `KillMode=control-group` on the daemons) so a stuck process can't stall a reboot for the 90s systemd default. Applied at all 7 template sites: `pos-communication-telegram-listener`, `pos-communication-matrix-listener` (also gained a `trap 'kill $(jobs -p) 2>/dev/null; exit 0' TERM INT` in `run_daemon` so stop returns sub-second), `pos-network-download` (aria2 + retry-healer units), `lib/scheduler-lib.sh` `sched_write_units`, `lib/entertainment-lib.sh` `write_units`, and `systemd/{ssh-agent,autostart,usb-automount}.service`. Legacy-unit cleanup: the repo no longer ships `pos-health.{service,timer}` / `pos-entertainment.service` (they were documented but postinstall never created them — found stale only on the live box, FAILED); removed their stale references from SYSTEMD.md (deleted the `pos-health.service` section + gating special-case, added a new **Stop behavior** section), POS.md, HOWTO.md, howto/system.md (now documents the `pos system schedule` job replacement + removal commands), AGENT_Context (tree, phase description, selfcontained table). DEV.md Best Practices gains a **Systemd units** convention (TimeoutStopSec=5s + TERM trap + regeneration caveat). Verified: `bash -n` on all edited scripts; `make gen && make check` green (filetable rows for the two listeners + network-download auto-regenerated, hand-maintained lib rows bumped 350→354 / 822→830). Live-box application is manual (this session was a Google Cloud Shell, not the real machine): regenerate units via `pos network download start`, `pos communication telegram listener --enable`, `pos system schedule enable <job>`, `pos entertainment enable <plugin>`, then `sudo systemctl disable --now pos-health.timer pos-health.service 2>/dev/null; sudo rm -f /etc/systemd/system/pos-health.{service,timer} && sudo systemctl daemon-reload`.
|
||||
|
||||
@@ -10,19 +10,19 @@
|
||||
|
||||
<!-- GEN:START docmap -->
|
||||
| ## 1. Project Overview | 28–43 |
|
||||
| ## 2. Directory Structure | 44–192 |
|
||||
| ## 3. Installation Flow | 193–244 |
|
||||
| ## 4. The `pos` CLI System | 245–314 |
|
||||
| ## 5. Shared Library — `lib/common.sh` | 315–346 |
|
||||
| ## 6. Docker Compose / ScaleTail | 347–389 |
|
||||
| ## 7. Optional Apps (`apps/`) | 390–419 |
|
||||
| ## 8. Entertainment Module | 420–433 |
|
||||
| ## 9. Systemd Services | 434–445 |
|
||||
| ## 10. Configuration Files | 446–472 |
|
||||
| ## 11. Coding Conventions | 473–505 |
|
||||
| ## 12. Development Workflow | 506–558 |
|
||||
| ## 13. Key File Quick Reference | 559–618 |
|
||||
| ## 14. Common Tasks for Agents | 619–648 |
|
||||
| ## 2. Directory Structure | 44–193 |
|
||||
| ## 3. Installation Flow | 194–245 |
|
||||
| ## 4. The `pos` CLI System | 246–316 |
|
||||
| ## 5. Shared Library — `lib/common.sh` | 317–348 |
|
||||
| ## 6. Docker Compose / ScaleTail | 349–391 |
|
||||
| ## 7. Optional Apps (`apps/`) | 392–421 |
|
||||
| ## 8. Entertainment Module | 422–435 |
|
||||
| ## 9. Systemd Services | 436–447 |
|
||||
| ## 10. Configuration Files | 448–474 |
|
||||
| ## 11. Coding Conventions | 475–507 |
|
||||
| ## 12. Development Workflow | 508–560 |
|
||||
| ## 13. Key File Quick Reference | 561–621 |
|
||||
| ## 14. Common Tasks for Agents | 622–652 |
|
||||
<!-- GEN:END docmap -->
|
||||
|
||||
## 1. Project Overview
|
||||
@@ -64,6 +64,7 @@ Linux_post_install/
|
||||
│ ├── pos-ai-gemini # Chat with Google Gemini (ask, chat, models, sessions)
|
||||
│ ├── pos-communication-matrix-listener # Matrix listener: map /command → bash, run them on room messages
|
||||
│ ├── pos-communication-matrix-sender # Send messages to a Matrix room via the client-server API (send, test, login)
|
||||
│ ├── pos-communication-scrcpy # Mirror/control an Android device via scrcpy+adb (mirror, devices, record, tcpip, connect, push, pull, screenshot, info)
|
||||
│ ├── pos-communication-telegram-listener # Telegram bot listener: map /command → bash, run them on chat messages
|
||||
│ ├── pos-communication-telegram-sender # Send Telegram messages/files/links/stickers via Bot API (send, test)
|
||||
│ ├── pos-config # Interactive editor for the tools' runtime config (reads # POS_CONFIG: registry)
|
||||
@@ -264,6 +265,7 @@ All non-interactive `pos` commands log output to `~/.local/share/linux_post_inst
|
||||
| ai | gemini | `pos-ai-gemini` | Chat with Google Gemini (ask, chat, models, sessions) |
|
||||
| communication | matrix-listener | `pos-communication-matrix-listener` | Matrix listener: map /command → bash, run them on room messages |
|
||||
| communication | matrix-sender | `pos-communication-matrix-sender` | Send messages to a Matrix room via the client-server API (send, test, login) |
|
||||
| communication | scrcpy | `pos-communication-scrcpy` | Mirror/control an Android device via scrcpy+adb (mirror, devices, record, tcpip, connect, push, pull, screenshot, info) |
|
||||
| communication | telegram-listener | `pos-communication-telegram-listener` | Telegram bot listener: map /command → bash, run them on chat messages |
|
||||
| communication | telegram-sender | `pos-communication-telegram-sender` | Send Telegram messages/files/links/stickers via Bot API (send, test) |
|
||||
| | config | `pos-config` | Interactive editor for the tools' runtime config (reads # POS_CONFIG: registry) |
|
||||
@@ -580,6 +582,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
||||
| `bin/pos-ai-gemini` | 311 | Chat with Google Gemini (ask, chat, models, sessions) |
|
||||
| `bin/pos-communication-matrix-listener` | 568 | Matrix listener: map /command → bash, run them on room messages |
|
||||
| `bin/pos-communication-matrix-sender` | 224 | Send messages to a Matrix room via the client-server API (send, test, login) |
|
||||
| `bin/pos-communication-scrcpy` | 239 | Mirror/control an Android device via scrcpy+adb (mirror, devices, record, tcpip, connect, push, pull, screenshot, info) |
|
||||
| `bin/pos-communication-telegram-listener` | 566 | Telegram bot listener: map /command → bash, run them on chat messages |
|
||||
| `bin/pos-communication-telegram-sender` | 221 | Send Telegram messages/files/links/stickers via Bot API (send, test) |
|
||||
| `bin/pos-config` | 80 | Interactive editor for the tools' runtime config (reads # POS_CONFIG: registry) |
|
||||
@@ -610,7 +613,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
||||
| `bin/pos-system-health` | 209 | Host health dashboard (disk, RAM, services, backup age, fail2ban, docker); exit 1 if any FAIL |
|
||||
| `bin/pos-system-schedule` | 81 | Scheduled jobs: run a command on a timer; notify on threshold/change/error/always or silently |
|
||||
| `bin/pos-tree` | 112 | Show the pos CLI command tree: categories, commands, and subcommands |
|
||||
| `completions/pos.bash` | 291 | Dynamic bash completion |
|
||||
| `completions/pos.bash` | 292 | Dynamic bash completion |
|
||||
<!-- GEN:END filetable -->
|
||||
| `apps/install.sh` | 171 | App install/uninstall picker/orchestrator |
|
||||
|
||||
@@ -639,6 +642,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
||||
| Modify aria2 download daemon / queue logic | Edit `bin/pos-network-download` |
|
||||
| Modify NFS share logic | Edit `bin/pos-share-nfs-server` / `bin/pos-share-nfs-client` |
|
||||
| Modify SMB share logic | Edit `bin/pos-share-smb-server` / `bin/pos-share-smb-client` |
|
||||
| Modify scrcpy mirroring logic | Edit `bin/pos-communication-scrcpy` (config scope `scrcpy` via `pos config scrcpy`; `SCRCPY_*` keys in `~/.config/linux_post_install/scrcpy.env`) |
|
||||
| Modify the scheduler / scheduled jobs | Edit `bin/pos-system-schedule` / `lib/scheduler-lib.sh` (jobs in `~/.config/linux_post_install/schedule.d/`) |
|
||||
| Modify AI/Gemini logic | Edit `bin/pos-ai-gemini` (config scope `ai` via `pos config ai`; `AI_GEMINI_API_KEY`/`AI_GEMINI_MODEL` in `~/.config/linux_post_install/ai.env`) |
|
||||
| Modify UFW/firewall logic | Edit `bin/pos-system-firewall` |
|
||||
|
||||
+2
-1
@@ -17,7 +17,7 @@ authoritative one-line reference (every command + flag), see
|
||||
| `pos system schedule` | Scheduled jobs: run a command on a timer, notify on threshold/change/error or silently | [schedule](howto/schedule.md) |
|
||||
| `pos ssh` | Load keys into the agent | [ssh](howto/ssh.md) |
|
||||
| `pos share` | Share USB devices & filesystems over the network (USB, NFS, SMB) | [share](howto/share.md) |
|
||||
| `pos communication` | Send Telegram/Matrix messages & alerts, /command listeners | [communication](howto/communication.md) |
|
||||
| `pos communication` | Send Telegram/Matrix messages & alerts, /command listeners, Android mirroring (scrcpy) | [communication](howto/communication.md) |
|
||||
| `pos entertainment` | Scheduled auto-messages from public APIs | [entertainment](howto/entertainment.md) |
|
||||
|
||||
Every tool is `bin/pos-<category>-<command>`; run `pos <category> --help` to
|
||||
@@ -37,6 +37,7 @@ templates (without overwriting an existing file):
|
||||
|------|---------|------|
|
||||
| `telegram.env` | `pos communication telegram sender` / `listener`, everything that alerts | `TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID` |
|
||||
| `matrix.env` | `pos communication matrix sender` / `listener` | `MATRIX_HOMESERVER`, `MATRIX_ACCESS_TOKEN`, `MATRIX_USER_ID`, `MATRIX_ROOM_ID` |
|
||||
| `scrcpy.env` | `pos communication scrcpy` | `SCRCPY_SERIAL`, `SCRCPY_MAX_SIZE`, `SCRCPY_MAX_FPS`, `SCRCPY_BIT_RATE`, `SCRCPY_FULLSCREEN`, `SCRCPY_RECORD_DIR`, `SCRCPY_PUSH_TARGET`, `SCRCPY_EXTRA_FLAGS` |
|
||||
| `notify.env` | `lib/notify.sh` (all alerting) | `NOTIFY_PLATFORM` (e.g. `telegram,matrix`) |
|
||||
| `system.env` | `pos system health`, `pos system backup` | `BACKUP_SERVICE_ROOTS`, `HEALTH_BACKUP_MAX_AGE_DAYS` |
|
||||
| `compose.env` | `pos docker compose` | `TS_AUTHKEY`, `TZ`, `DNS_SERVER`, `SERVICES_BASE` |
|
||||
|
||||
+32
@@ -265,6 +265,7 @@ Subcommands that need input prompt interactively when args are omitted.
|
||||
| `pos communication telegram listener` | `bin/pos-communication-telegram-listener` | Telegram bot listener: map `/command` → bash commands and run them from chat; interactive editor for the map | Same `telegram.env` (the bot is the owner, `TELEGRAM_CHAT_ID`). Map lives in `~/.config/linux_post_install/telegram_commands.env` (`/cmd=bash command` lines, chmod 600) |
|
||||
| `pos communication matrix sender send "text"` | `bin/pos-communication-matrix-sender` | Send a text message (plain or `--markdown`) to a Matrix room via the client-server API; also `login` (password → access token) and `test` | Homeserver + room from `~/.config/linux_post_install/matrix.env` (`MATRIX_HOMESERVER`, `MATRIX_ACCESS_TOKEN`, `MATRIX_USER_ID`, `MATRIX_ROOM_ID`, chmod 600, secrets masked by `pos config matrix`). Precedence: `--room` flag > env > config file |
|
||||
| `pos communication matrix listener` | `bin/pos-communication-matrix-listener` | Matrix listener: map `/command` → bash commands and run them from room messages; interactive editor for the map | Same `matrix.env` (reacts to `MATRIX_USER_ID`'s own messages; watches `MATRIX_ROOM_ID` or all joined rooms). Map lives in `~/.config/linux_post_install/matrix_commands.env` (`/cmd=bash command` lines, chmod 600) |
|
||||
| `pos communication scrcpy [cmd]` | `bin/pos-communication-scrcpy` | Mirror/control an Android device via scrcpy+adb: `devices`, `record`, `tcpip`, `connect`, `push`, `pull`, `screenshot`, `info` (bare = mirror) | `scrcpy.env` (`SCRCPY_SERIAL`, `SCRCPY_MAX_SIZE`, `SCRCPY_MAX_FPS`, `SCRCPY_BIT_RATE`, `SCRCPY_FULLSCREEN`, `SCRCPY_RECORD_DIR`, `SCRCPY_PUSH_TARGET`, `SCRCPY_EXTRA_FLAGS`) via `pos config scrcpy` |
|
||||
|
||||
`pos communication telegram sender` in detail:
|
||||
|
||||
@@ -322,6 +323,37 @@ The access token is a secret — it is stored only in `~/.config/linux_post_inst
|
||||
|
||||
The daemon long-polls `/sync` (30s timeout, per-sync `since` token, compact filter that drops presence/account_data/device noise and only requests `m.room.message` timeline events). It reacts only to messages **from `MATRIX_USER_ID`** (your own account — resolved via `/account/whoami` if unset); a `MATRIX_ROOM_ID` restricts it to one room, otherwise every joined room is watched. `/` and `!` prefixes both resolve (`!status` = `/status`). `/help` lists mapped commands; an unmapped command replies "Unknown command". Non-command text starting with `ai ` (case-insensitive, e.g. `ai what is Nvidia`) is forwarded to Gemini via `pos ai gemini ask` with a per-room session (`matrix-<room>`; `ai /reset` clears it) and the answer is replied verbatim with markdown stripped. Replies are sent as `m.text` threaded with `m.in_reply_to` on your message. Commands run as your user via `timeout 60 bash -c "…"` (stdout + stderr are replied, truncated to ~3800 chars; empty output → `OK`; non-zero exit is prefixed with `exit <rc>`), so `sudo` inside them needs a NOPASSWD rule. A map value prefixed with `@quiet ` runs the command but does NOT reply — for commands that already send their own notification (e.g. `/status=@quiet pos system health --send`). Map lines may carry a `/cmd::description=…` description. `--enable` warns if linger is off — the service stops when you log out unless you run `sudo loginctl enable-linger $(whoami)`.
|
||||
|
||||
`pos communication scrcpy` in detail:
|
||||
|
||||
| Command | Behavior |
|
||||
|---------|----------|
|
||||
| `pos communication scrcpy` | Mirror the device: opens the scrcpy window (needs a display — over ssh use `ssh -X`). Built from `scrcpy.env` defaults plus any pass-through scrcpy flags (`pos communication scrcpy --turn-screen-off --stay-awake`) |
|
||||
| `pos communication scrcpy devices` | `adb devices -l` — the source of serials for `SCRCPY_SERIAL` |
|
||||
| `pos communication scrcpy record [file] [--headless]` | Record a session to an mp4 — default `$SCRCPY_RECORD_DIR/<device>_<date>.mp4`; `--headless` adds `--no-playback` (no window — headless-server friendly) |
|
||||
| `pos communication scrcpy tcpip [port]` | `adb tcpip <port>` (default 5555) — switch the USB device to wireless adb, prints the reconnect command with the detected device IP |
|
||||
| `pos communication scrcpy connect <ip[:port]>` | `adb connect` then mirror over WiFi (`-s <ip:port>`) |
|
||||
| `pos communication scrcpy push <local> [remote]` | `adb push` — default destination `$SCRCPY_PUSH_TARGET` (`/sdcard/Download`, scrcpy's own default) |
|
||||
| `pos communication scrcpy pull <remote> [local]` | `adb pull` — default local dir is the current directory |
|
||||
| `pos communication scrcpy screenshot [file]` | `adb exec-out screencap -p` → a PNG, default `$SCRCPY_RECORD_DIR/<device>_<date>.png` |
|
||||
| `pos communication scrcpy info` | Device model, Android version, SDK, serial (`adb shell getprop`) |
|
||||
|
||||
A device must have **USB debugging** enabled (Developer options) and the phone's "allow USB debugging" dialog accepted on first connect. `devices`, `record --headless`, `tcpip`, `connect`, `push`/`pull`, `screenshot`, `info` work without a display; the bare mirror needs one.
|
||||
|
||||
**Configuration** (`~/.config/linux_post_install/scrcpy.env`, edit with `pos config scrcpy`):
|
||||
|
||||
| Key | Required | Default | Purpose |
|
||||
|-----|----------|---------|---------|
|
||||
| `SCRCPY_SERIAL` | no | — | Default device serial/`ip:port` (from `devices`) — passed as `-s` to adb/scrcpy |
|
||||
| `SCRCPY_MAX_SIZE` | no | — | Limit video size, e.g. `1920` (scrcpy `--max-size`) |
|
||||
| `SCRCPY_MAX_FPS` | no | — | Limit frame rate, e.g. `60` (scrcpy `--max-fps`) |
|
||||
| `SCRCPY_BIT_RATE` | no | — | Video bit rate, e.g. `8M` (scrcpy `--video-bit-rate`) |
|
||||
| `SCRCPY_FULLSCREEN` | no | `false` | `true` adds `--fullscreen` |
|
||||
| `SCRCPY_RECORD_DIR` | no | `~/Videos/scrcpy` | Output dir for `record`/`screenshot` defaults |
|
||||
| `SCRCPY_PUSH_TARGET` | no | `/sdcard/Download` | Default `adb push` destination |
|
||||
| `SCRCPY_EXTRA_FLAGS` | no | — | Extra scrcpy flags appended to every mirror |
|
||||
|
||||
Requires `scrcpy` + `adb` (added to `preinstall.sh` PACKAGES). The apt `scrcpy` build is older than the latest release — the optional app `apps/media/scrcpy.sh` installs the current GitHub release (bundles `adb`); run it via `./install.sh --apps` or directly.
|
||||
|
||||
### entertainment
|
||||
|
||||
**File:** `bin/pos-entertainment-send`
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# How-To: `pos communication`
|
||||
|
||||
Messaging and alerts over Telegram and Matrix. Tools: `telegram-sender`,
|
||||
`telegram-listener`, `matrix-sender`, `matrix-listener`.
|
||||
Messaging and alerts over Telegram and Matrix, plus Android mirroring. Tools:
|
||||
`telegram-sender`, `telegram-listener`, `matrix-sender`, `matrix-listener`,
|
||||
`scrcpy`.
|
||||
|
||||
| Tool | What it does |
|
||||
|------|--------------|
|
||||
@@ -9,6 +10,7 @@ Messaging and alerts over Telegram and Matrix. Tools: `telegram-sender`,
|
||||
| `pos communication telegram listener` | Bot listener: map `/command` → bash and run it from chat (systemd user daemon) |
|
||||
| `pos communication matrix sender` | Send messages to a Matrix room via the client-server API (send, test, login) |
|
||||
| `pos communication matrix listener` | Matrix listener: map `/command` → bash and run it from room messages (systemd user daemon) |
|
||||
| `pos communication scrcpy` | Mirror/control an Android device over USB or WiFi (scrcpy+adb) |
|
||||
|
||||
`telegram-sender` is the workhorse: it backs the whole **notify system** —
|
||||
health digests, backup alerts, firewall changes — and can be used directly.
|
||||
@@ -227,6 +229,77 @@ pos communication matrix listener --disable # remove it
|
||||
|
||||
---
|
||||
|
||||
## `pos communication scrcpy`
|
||||
|
||||
Mirror and control an Android device from the PC — the phone's screen in a
|
||||
window, controlled with mouse + keyboard (scrcpy by Genymobile, no root, no
|
||||
phone app). Works over USB or WiFi.
|
||||
|
||||
### One-time setup
|
||||
|
||||
```bash
|
||||
# phone: Settings → About → tap "Build number" 7× → Developer options → enable
|
||||
# "USB debugging"; plug it in and accept the "Allow USB debugging" dialog
|
||||
pos communication scrcpy devices # confirm the phone shows as "device"
|
||||
pos communication scrcpy info # model / Android version
|
||||
pos config scrcpy # optional defaults (serial, size, fps, ...)
|
||||
```
|
||||
|
||||
Requires `scrcpy` + `adb` (both in preinstall PACKAGES). The apt build is older
|
||||
than the latest release — install the current GitHub release (bundles `adb`)
|
||||
with the optional app `apps/media/scrcpy.sh` (or `./install.sh --apps`).
|
||||
|
||||
### Mirror
|
||||
|
||||
```bash
|
||||
pos communication scrcpy # USB device, config defaults
|
||||
pos communication scrcpy --turn-screen-off # pass any scrcpy flag through
|
||||
pos communication scrcpy --no-audio --always-on-top
|
||||
```
|
||||
|
||||
The window needs a display — over ssh use `ssh -X` (and a phone already
|
||||
reachable over WiFi, see below). `scrcpy --help` lists every flag; the wrapper
|
||||
forwards flags verbatim.
|
||||
|
||||
### Wireless (no USB cable)
|
||||
|
||||
```bash
|
||||
pos communication scrcpy tcpip 5555 # switch the USB device to WiFi adb
|
||||
# unplug the phone, then:
|
||||
pos communication scrcpy connect 192.168.1.42:5555 # connect + mirror
|
||||
```
|
||||
|
||||
`tcpip` prints the exact `connect` command with the phone's detected IP. Set
|
||||
`SCRCPY_SERIAL` in `pos config scrcpy` so later bare `pos communication scrcpy`
|
||||
goes straight to that device.
|
||||
|
||||
### Record / screenshot / files
|
||||
|
||||
```bash
|
||||
pos communication scrcpy record --headless # record to ~/Videos/scrcpy/, no window
|
||||
pos communication scrcpy record clip.mp4 # explicit file
|
||||
pos communication scrcpy screenshot # PNG to ~/Videos/scrcpy/
|
||||
pos communication scrcpy push ~/app.apk # → /sdcard/Download/
|
||||
pos communication scrcpy pull /sdcard/DCIM/Camera ~/photos
|
||||
```
|
||||
|
||||
These all work headless — handy on the homelab box for grabbing a phone's
|
||||
screen/file without a desktop.
|
||||
|
||||
**Troubleshooting:**
|
||||
- "no device connected" → is USB debugging on, is the "Allow USB debugging"
|
||||
dialog accepted, and does `pos communication scrcpy devices` show the serial?
|
||||
- Device shows `offline`/`unauthorized` → re-accept the USB debugging dialog on
|
||||
the phone (unplug/replug); `adb kill-server` may help.
|
||||
- "not reachable" after `tcpip` → the phone's WiFi IP changed; re-check with
|
||||
`adb devices` or run `tcpip` again while plugged in.
|
||||
- Mirror window is blank / no audio → older apt scrcpy lacks features; install
|
||||
the latest with the `apps/media/scrcpy.sh` app installer.
|
||||
- `ssh -X` mirror is slow → prefer WiFi or a wired LAN; bump `SCRCPY_MAX_FPS`
|
||||
down or set `SCRCPY_BIT_RATE` lower in `pos config scrcpy`.
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- Reference + config file details: [DOC/POS.md → communication](../POS.md)
|
||||
|
||||
Executable
+239
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
# POS: communication scrcpy — Mirror/control an Android device via scrcpy+adb (mirror, devices, record, tcpip, connect, push, pull, screenshot, info)
|
||||
# POS_SUBCMDS: devices record tcpip connect push pull screenshot info
|
||||
# POS_CONFIG: scrcpy | scrcpy.env | SCRCPY_SERIAL=:Default adb serial (from 'pos communication scrcpy devices') | SCRCPY_MAX_SIZE=:Limit video size (e.g. 1920) | SCRCPY_MAX_FPS=:Limit frame rate (e.g. 60) | SCRCPY_BIT_RATE=:Video bit rate (e.g. 8M) | SCRCPY_FULLSCREEN=:Start fullscreen (true/false) | SCRCPY_RECORD_DIR=:Record output dir (default ~/Videos/scrcpy) | SCRCPY_PUSH_TARGET=:adb push destination (default /sdcard/Download) | SCRCPY_EXTRA_FLAGS=:Extra scrcpy flags appended to every mirror
|
||||
|
||||
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
||||
|
||||
CONFIG_FILE="$HOME/.config/linux_post_install/scrcpy.env"
|
||||
|
||||
command -v scrcpy &>/dev/null || err "scrcpy not found — install it: 'sudo apt install scrcpy' (or the latest release via the app installer 'apps/media/scrcpy.sh', see 'pos help communication scrcpy')"
|
||||
command -v adb &>/dev/null || err "adb not found — install it: 'sudo apt install adb'"
|
||||
|
||||
load_config() {
|
||||
[ -f "$CONFIG_FILE" ] || return 0
|
||||
local k v
|
||||
while IFS='=' read -r k v; do
|
||||
[ -n "$k" ] || continue
|
||||
case "$k" in
|
||||
\#*) continue ;;
|
||||
esac
|
||||
v="${v%\"}"; v="${v#\"}"; v="${v%\'}"; v="${v#\'}"
|
||||
v="${v//$'\r'/}"
|
||||
if [ -z "${!k:-}" ]; then
|
||||
export "$k"="$v"
|
||||
fi
|
||||
done < <(grep -E '^[A-Z_]+=' "$CONFIG_FILE" || true)
|
||||
}
|
||||
|
||||
load_config
|
||||
SCRCPY_SERIAL="${SCRCPY_SERIAL:-}"
|
||||
SCRCPY_MAX_SIZE="${SCRCPY_MAX_SIZE:-}"
|
||||
SCRCPY_MAX_FPS="${SCRCPY_MAX_FPS:-}"
|
||||
SCRCPY_BIT_RATE="${SCRCPY_BIT_RATE:-}"
|
||||
SCRCPY_FULLSCREEN="${SCRCPY_FULLSCREEN:-false}"
|
||||
SCRCPY_RECORD_DIR="${SCRCPY_RECORD_DIR:-$HOME/Videos/scrcpy}"
|
||||
SCRCPY_PUSH_TARGET="${SCRCPY_PUSH_TARGET:-/sdcard/Download}"
|
||||
SCRCPY_EXTRA_FLAGS="${SCRCPY_EXTRA_FLAGS:-}"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: pos communication scrcpy [command] [args]
|
||||
|
||||
Mirror and control an Android device with scrcpy (a GUI window; needs a
|
||||
display — over ssh use 'ssh -X'). Over USB with USB debugging enabled, or
|
||||
wirelessly via 'tcpip'/'connect'.
|
||||
|
||||
Commands:
|
||||
(none) Mirror the device (config defaults + pass-through)
|
||||
devices List connected devices (adb devices -l)
|
||||
record [file] [--headless] Record a session to an mp4 (default:
|
||||
\$SCRCPY_RECORD_DIR/<device>_<date>.mp4).
|
||||
--headless adds --no-playback (no window)
|
||||
tcpip [port] Switch the USB device to wireless adb (default 5555)
|
||||
connect <ip[:port]> adb connect + mirror over WiFi
|
||||
push <local> [remote] Copy a file/folder to the device (default
|
||||
\$SCRCPY_PUSH_TARGET, i.e. /sdcard/Download)
|
||||
pull <remote> [local] Copy a file/folder from the device (default: cwd)
|
||||
screenshot [file] Save a PNG of the screen (default:
|
||||
\$SCRCPY_RECORD_DIR/<date>.png)
|
||||
info Show device model, Android version, SDK, serial
|
||||
|
||||
Pass-through: any scrcpy flag given to the bare form is forwarded verbatim
|
||||
(e.g. 'pos communication scrcpy --turn-screen-off --stay-awake').
|
||||
All scrcpy options: run 'scrcpy --help'.
|
||||
|
||||
Config: $CONFIG_FILE (SCRCPY_*) — edit it with 'pos config scrcpy'
|
||||
Precedence: CLI flags > environment > config file.
|
||||
|
||||
Examples:
|
||||
pos communication scrcpy
|
||||
pos communication scrcpy --no-audio --always-on-top
|
||||
pos communication scrcpy devices
|
||||
pos communication scrcpy record --headless
|
||||
pos communication scrcpy tcpip 5555
|
||||
pos communication scrcpy connect 192.168.1.42:5555
|
||||
pos communication scrcpy push ~/file.apk
|
||||
pos communication scrcpy pull /sdcard/DCIM/Camera
|
||||
pos communication scrcpy screenshot shot.png
|
||||
pos communication scrcpy info
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
-h|--help) usage ;;
|
||||
esac
|
||||
|
||||
# ── helpers ────────────────────────────────────────────────────
|
||||
_sflag() { [ -n "$SCRCPY_SERIAL" ] && printf -- '-s %s' "$SCRCPY_SERIAL"; }
|
||||
|
||||
# Print the serial of the first reachable device, or nothing.
|
||||
_first_device() {
|
||||
adb devices 2>/dev/null | awk 'NR>1 && $2=="device" {print $1; exit}'
|
||||
}
|
||||
|
||||
# Exit 1 with a friendly hint when the target device isn't reachable.
|
||||
_require_device() {
|
||||
local serial="${1:-$SCRCPY_SERIAL}"
|
||||
if [ -n "$serial" ]; then
|
||||
if ! adb -s "$serial" get-state 2>/dev/null | grep -qx device; then
|
||||
err "device '$serial' not reachable — is USB debugging enabled? (plug it in, or 'pos communication scrcpy connect <ip>')"
|
||||
fi
|
||||
elif [ -z "$(_first_device)" ]; then
|
||||
err "no device connected — 'pos communication scrcpy devices' to list, or 'pos communication scrcpy connect <ip[:port]>' for wireless"
|
||||
fi
|
||||
}
|
||||
|
||||
_dev_name() { printf '%s' "${SCRCPY_SERIAL:-device}" | tr ':' '-'; }
|
||||
|
||||
_extra_flags() {
|
||||
local -a extra
|
||||
[ -n "$SCRCPY_EXTRA_FLAGS" ] && read -r -a extra <<< "$SCRCPY_EXTRA_FLAGS"
|
||||
printf '%s\n' "${extra[@]+"${extra[@]}"}"
|
||||
}
|
||||
|
||||
# Build the scrcpy command from config defaults + pass-through args.
|
||||
_mirror() { # $@ = pass-through scrcpy flags
|
||||
local -a cmd=(scrcpy)
|
||||
local f
|
||||
[ -n "$SCRCPY_SERIAL" ] && cmd+=(-s "$SCRCPY_SERIAL")
|
||||
[ -n "$SCRCPY_MAX_SIZE" ] && cmd+=(--max-size "$SCRCPY_MAX_SIZE")
|
||||
[ -n "$SCRCPY_MAX_FPS" ] && cmd+=(--max-fps "$SCRCPY_MAX_FPS")
|
||||
[ -n "$SCRCPY_BIT_RATE" ] && cmd+=(--video-bit-rate "$SCRCPY_BIT_RATE")
|
||||
[ "$SCRCPY_FULLSCREEN" = "true" ] && cmd+=(--fullscreen)
|
||||
while IFS= read -r f; do cmd+=("$f"); done < <(_extra_flags)
|
||||
cmd+=("$@")
|
||||
exec "${cmd[@]}"
|
||||
}
|
||||
|
||||
# ── subcommands ────────────────────────────────────────────────
|
||||
cmd_devices() {
|
||||
adb devices -l
|
||||
}
|
||||
|
||||
cmd_record() {
|
||||
_require_device
|
||||
local headless=0 file=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--headless) headless=1 ;;
|
||||
-*) err "unknown record option: $1 (options: [file] [--headless])" ;;
|
||||
*) [ -z "$file" ] && file="$1" || err "too many arguments: $1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
mkdir -p "$SCRCPY_RECORD_DIR"
|
||||
file="${file:-$SCRCPY_RECORD_DIR/$(_dev_name)_$(date +%Y%m%d_%H%M%S).mp4}"
|
||||
log "recording to $file"
|
||||
local -a cmd=(scrcpy $(_sflag) "--record=$file")
|
||||
[ "$headless" -eq 1 ] && cmd+=(--no-playback)
|
||||
exec "${cmd[@]}"
|
||||
}
|
||||
|
||||
cmd_tcpip() {
|
||||
_require_device
|
||||
local port="${1:-5555}"
|
||||
[[ "$port" =~ ^[0-9]+$ ]] || err "port must be a number (got '$port')"
|
||||
adb $(_sflag) tcpip "$port"
|
||||
local ip
|
||||
ip="$(adb $(_sflag) shell 'ip route get 1.1.1.1 2>/dev/null' 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}')"
|
||||
if [ -n "$ip" ]; then
|
||||
log "device is now listening on tcpip port $port — reconnect wirelessly with:"
|
||||
log " pos communication scrcpy connect $ip:$port"
|
||||
else
|
||||
log "device is now listening on tcpip port $port — reconnect wirelessly with:"
|
||||
log " pos communication scrcpy connect <device-ip>:$port"
|
||||
warn "could not auto-detect the device IP — find it with 'adb $(_sflag) shell ip addr'"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_connect() {
|
||||
local target="${1:-}"
|
||||
[ -n "$target" ] || err "usage: pos communication scrcpy connect <ip[:port]>"
|
||||
log "adb connect $target"
|
||||
adb connect "$target"
|
||||
SCRCPY_SERIAL="$target"
|
||||
_require_device "$target"
|
||||
_mirror
|
||||
}
|
||||
|
||||
cmd_push() {
|
||||
_require_device
|
||||
local local_path="${1:-}" remote="${2:-$SCRCPY_PUSH_TARGET}"
|
||||
[ -n "$local_path" ] || err "usage: pos communication scrcpy push <local> [remote]"
|
||||
[ -e "$local_path" ] || err "no such file or folder: $local_path"
|
||||
log "adb push $local_path → $remote"
|
||||
adb $(_sflag) push "$local_path" "$remote"
|
||||
}
|
||||
|
||||
cmd_pull() {
|
||||
_require_device
|
||||
local remote="${1:-}" local_path="${2:-.}"
|
||||
[ -n "$remote" ] || err "usage: pos communication scrcpy pull <remote> [local]"
|
||||
log "adb pull $remote → $local_path"
|
||||
adb $(_sflag) pull "$remote" "$local_path"
|
||||
}
|
||||
|
||||
cmd_screenshot() {
|
||||
_require_device
|
||||
local file="${1:-}"
|
||||
if [ -z "$file" ]; then
|
||||
mkdir -p "$SCRCPY_RECORD_DIR"
|
||||
file="$SCRCPY_RECORD_DIR/$(_dev_name)_$(date +%Y%m%d_%H%M%S).png"
|
||||
fi
|
||||
log "screenshot → $file"
|
||||
adb $(_sflag) exec-out screencap -p > "$file"
|
||||
}
|
||||
|
||||
cmd_info() {
|
||||
_require_device
|
||||
local model android sdk serial
|
||||
model="$(adb $(_sflag) shell getprop ro.product.model | tr -d '\r')"
|
||||
android="$(adb $(_sflag) shell getprop ro.build.version.release | tr -d '\r')"
|
||||
sdk="$(adb $(_sflag) shell getprop ro.build.version.sdk | tr -d '\r')"
|
||||
serial="$(adb $(_sflag) shell getprop ro.serialno | tr -d '\r')"
|
||||
printf '%-12s %s\n' "model:" "${model:-unknown}"
|
||||
printf '%-12s %s\n' "android:" "${android:-unknown}"
|
||||
printf '%-12s %s\n' "sdk:" "${sdk:-unknown}"
|
||||
printf '%-12s %s\n' "serial:" "${serial:-unknown}"
|
||||
}
|
||||
|
||||
# ── dispatch ───────────────────────────────────────────────────
|
||||
cmd="${1:-}"
|
||||
case "$cmd" in
|
||||
devices) shift; cmd_devices "$@" ;;
|
||||
record) shift; cmd_record "$@" ;;
|
||||
tcpip) shift; cmd_tcpip "$@" ;;
|
||||
connect) shift; cmd_connect "$@" ;;
|
||||
push) shift; cmd_push "$@" ;;
|
||||
pull) shift; cmd_pull "$@" ;;
|
||||
screenshot) shift; cmd_screenshot "$@" ;;
|
||||
info) shift; cmd_info "$@" ;;
|
||||
*)
|
||||
# Anything else is a pass-through scrcpy flag for the mirror command.
|
||||
_require_device
|
||||
_mirror "$@"
|
||||
;;
|
||||
esac
|
||||
@@ -22,6 +22,7 @@ _pos_flags[tree]="--depth"
|
||||
declare -A _pos_subcmds
|
||||
_pos_subcmds[ai-gemini]="ask chat models sessions"
|
||||
_pos_subcmds[communication-matrix-sender]="send test login"
|
||||
_pos_subcmds[communication-scrcpy]="devices record tcpip connect push pull screenshot info"
|
||||
_pos_subcmds[communication-telegram-sender]="send test"
|
||||
_pos_subcmds[docker-compose]="ls installed up down restart logs update config"
|
||||
_pos_subcmds[docker-vbox]="create enter stop start rm ls"
|
||||
@@ -29,7 +30,7 @@ _pos_subcmds[network-download]="start stop status add torrent metalink list info
|
||||
_pos_subcmds[system-schedule]="run list config enable disable status migrate"
|
||||
# GEN:END possubcmds
|
||||
# GEN:START posconfigscopes
|
||||
declare -a _pos_config_scopes=(ai compose entertainment matrix notify system telegram)
|
||||
declare -a _pos_config_scopes=(ai compose entertainment matrix notify scrcpy system telegram)
|
||||
# GEN:END posconfigscopes
|
||||
|
||||
_pos() {
|
||||
|
||||
@@ -38,6 +38,7 @@ PACKAGES=(
|
||||
sysstat iotop atop vnstat
|
||||
python3 python3-pip rclone
|
||||
libqrencode4 libgtk-3-0
|
||||
scrcpy adb
|
||||
)
|
||||
|
||||
spawn "apt update" sudo apt update
|
||||
|
||||
Reference in New Issue
Block a user