@myerscarpenter/quest-dev 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -1
- package/build/commands/battery.js +3 -3
- package/build/commands/battery.js.map +1 -1
- package/build/commands/logcat.d.ts.map +1 -1
- package/build/commands/logcat.js +8 -6
- package/build/commands/logcat.js.map +1 -1
- package/build/commands/stay-awake.d.ts +31 -4
- package/build/commands/stay-awake.d.ts.map +1 -1
- package/build/commands/stay-awake.js +160 -83
- package/build/commands/stay-awake.js.map +1 -1
- package/build/index.js +44 -12
- package/build/index.js.map +1 -1
- package/build/utils/adb.d.ts +10 -3
- package/build/utils/adb.d.ts.map +1 -1
- package/build/utils/adb.js +10 -13
- package/build/utils/adb.js.map +1 -1
- package/build/utils/config.d.ts +19 -0
- package/build/utils/config.d.ts.map +1 -0
- package/build/utils/config.js +59 -0
- package/build/utils/config.js.map +1 -0
- package/build/utils/exec.d.ts.map +1 -1
- package/build/utils/exec.js +0 -2
- package/build/utils/exec.js.map +1 -1
- package/package.json +2 -2
- package/src/commands/battery.ts +3 -3
- package/src/commands/logcat.ts +7 -5
- package/src/commands/stay-awake.ts +184 -96
- package/src/index.ts +49 -14
- package/src/utils/adb.ts +17 -13
- package/src/utils/config.ts +66 -0
- package/src/utils/exec.ts +0 -2
- package/tests/config.test.ts +88 -0
- package/tests/exec.test.ts +3 -3
- package/tests/stay-awake.test.ts +71 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# quest-dev
|
|
2
2
|
|
|
3
|
-
CLI tools for Meta Quest
|
|
3
|
+
CLI tools for Meta Quest development — stay awake, screenshots, URL opening, logcat, and battery via ADB.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -55,12 +55,76 @@ This command:
|
|
|
55
55
|
|
|
56
56
|
Port forwarding is idempotent - safe to run multiple times without issues.
|
|
57
57
|
|
|
58
|
+
### Stay Awake
|
|
59
|
+
|
|
60
|
+
Keep your Quest awake during development by disabling autosleep, guardian, and system dialogs using the Meta Scriptable Testing API.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
quest-dev stay-awake --pin 1234
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Config file** — avoid passing `--pin` every time by creating `.quest-dev.json` in your project or `~/.config/quest-dev/config.json` globally:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"pin": "1234",
|
|
71
|
+
"idleTimeout": 300000,
|
|
72
|
+
"lowBattery": 10
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Flags:**
|
|
77
|
+
|
|
78
|
+
- `--status` — show current Scriptable Testing properties without changing anything
|
|
79
|
+
- `--disable` — manually disable test mode (restore all properties)
|
|
80
|
+
- `--idle-timeout <ms>` — auto-exit after inactivity (default: 300000ms / 5min)
|
|
81
|
+
- `--low-battery <percent>` — auto-exit when battery drops below threshold (default: 10%)
|
|
82
|
+
- `--verbose` — log battery status every 60s instead of only on 5% boundary crossings
|
|
83
|
+
|
|
84
|
+
**Activity signaling** — reset the idle timer from another process:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
kill -USR1 $(cat ~/.quest-dev-stay-awake.pid)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Watchdog** — a child process monitors the parent PID and automatically restores Quest settings if the parent dies (terminal close, `kill`, etc.), preventing a drained battery.
|
|
91
|
+
|
|
92
|
+
Requires Quest OS v44+ and your Meta Store PIN.
|
|
93
|
+
|
|
94
|
+
### Logcat
|
|
95
|
+
|
|
96
|
+
Capture Android logcat output for Quest debugging. Quest's ring buffer fills fast under VR load, so always start capture before testing.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
quest-dev logcat start # start capturing (clears ring buffer first)
|
|
100
|
+
quest-dev logcat start --filter "Unity:V" # capture with filter
|
|
101
|
+
quest-dev logcat status # check if capturing
|
|
102
|
+
quest-dev logcat tail # tail the current log file
|
|
103
|
+
quest-dev logcat stop # stop capturing, show file info
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Log files are saved to `./logs/logcat/` with a `latest.txt` symlink.
|
|
107
|
+
|
|
108
|
+
### Battery
|
|
109
|
+
|
|
110
|
+
Show Quest battery level and charging status:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
quest-dev battery
|
|
114
|
+
```
|
|
115
|
+
|
|
58
116
|
## How It Works
|
|
59
117
|
|
|
60
118
|
- **screenshot**: Triggers `com.oculus.metacam/.capture.CaptureService` via ADB, waits for the JPEG to be fully written (by checking for the EOI marker), pulls the file, then deletes it from the Quest
|
|
61
119
|
|
|
62
120
|
- **open**: Uses ADB for port forwarding and browser launching. If `cdp-cli` is installed, it uses CDP to intelligently reuse existing tabs instead of opening new ones.
|
|
63
121
|
|
|
122
|
+
- **stay-awake**: Uses the Meta Scriptable Testing API (`content://com.oculus.rc`) to disable autosleep, guardian, and dialogs. A watchdog child process ensures cleanup on exit.
|
|
123
|
+
|
|
124
|
+
- **logcat**: Spawns a background `adb logcat` process writing to timestamped files. Clears the ring buffer on start to avoid stale data.
|
|
125
|
+
|
|
126
|
+
- **battery**: Reads battery level and charging state via `adb shell dumpsys battery`.
|
|
127
|
+
|
|
64
128
|
## Development
|
|
65
129
|
|
|
66
130
|
```bash
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Quest battery command
|
|
3
3
|
* Shows battery percentage and charging status
|
|
4
4
|
*/
|
|
5
|
-
import { checkADBPath,
|
|
5
|
+
import { checkADBPath, getBatteryInfo, formatBatteryInfo } from '../utils/adb.js';
|
|
6
6
|
/**
|
|
7
7
|
* Main battery command handler
|
|
8
8
|
*/
|
|
@@ -25,7 +25,7 @@ export async function batteryCommand() {
|
|
|
25
25
|
process.exit(1);
|
|
26
26
|
}
|
|
27
27
|
// Get and display battery status
|
|
28
|
-
const
|
|
29
|
-
console.log(
|
|
28
|
+
const info = await getBatteryInfo();
|
|
29
|
+
console.log(formatBatteryInfo(info));
|
|
30
30
|
}
|
|
31
31
|
//# sourceMappingURL=battery.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"battery.js","sourceRoot":"","sources":["../../src/commands/battery.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"battery.js","sourceRoot":"","sources":["../../src/commands/battery.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAElF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,+BAA+B;IAC/B,YAAY,EAAE,CAAC;IAEf,uCAAuC;IACvC,IAAI,CAAC;QACH,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC;QAEvF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;YACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,iCAAiC;IACjC,MAAM,IAAI,GAAG,MAAM,cAAc,EAAE,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logcat.d.ts","sourceRoot":"","sources":["../../src/commands/logcat.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8GH;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEjE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA+BjD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"logcat.d.ts","sourceRoot":"","sources":["../../src/commands/logcat.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8GH;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEjE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA+BjD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAkCnD;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBjD"}
|
package/build/commands/logcat.js
CHANGED
|
@@ -9,7 +9,7 @@ import { join } from 'path';
|
|
|
9
9
|
import { readFileSync, writeFileSync, unlinkSync, existsSync, mkdirSync, symlinkSync, statSync, readlinkSync, openSync } from 'fs';
|
|
10
10
|
import { spawn } from 'child_process';
|
|
11
11
|
import { checkADBPath, checkADBDevices } from '../utils/adb.js';
|
|
12
|
-
import { execCommand
|
|
12
|
+
import { execCommand } from '../utils/exec.js';
|
|
13
13
|
const LOG_DIR = process.env.LOG_DIR || 'logs/logcat';
|
|
14
14
|
const PID_FILE = join(LOG_DIR, '.logcat_pid');
|
|
15
15
|
const LOGFILE_LINK = join(LOG_DIR, 'latest.txt');
|
|
@@ -226,11 +226,13 @@ export async function statusCommand() {
|
|
|
226
226
|
console.log('');
|
|
227
227
|
console.log('Recent logs:');
|
|
228
228
|
try {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
229
|
+
const { readdirSync } = await import('fs');
|
|
230
|
+
const files = readdirSync(LOG_DIR)
|
|
231
|
+
.filter(f => f.endsWith('.txt'))
|
|
232
|
+
.map(f => ({ name: f, mtime: statSync(join(LOG_DIR, f)).mtimeMs }))
|
|
233
|
+
.sort((a, b) => b.mtime - a.mtime)
|
|
234
|
+
.slice(0, 5);
|
|
235
|
+
files.forEach(f => console.log(' ' + f.name));
|
|
234
236
|
}
|
|
235
237
|
catch {
|
|
236
238
|
// Ignore
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logcat.js","sourceRoot":"","sources":["../../src/commands/logcat.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAW,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACnI,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"logcat.js","sourceRoot":"","sources":["../../src/commands/logcat.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Quest stay-awake command
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Uses Meta Scriptable Testing API (content://com.oculus.rc) to disable
|
|
4
|
+
* autosleep, guardian, and system dialogs for automated testing.
|
|
5
|
+
*
|
|
6
|
+
* Cleanup is critical: with autosleep disabled, the headset drains battery
|
|
7
|
+
* quickly. A watchdog child process ensures cleanup happens even if the
|
|
8
|
+
* parent is killed (TaskStop, terminal close, claude code exit).
|
|
5
9
|
*/
|
|
10
|
+
export interface TestProperties {
|
|
11
|
+
disable_guardian: boolean;
|
|
12
|
+
disable_dialogs: boolean;
|
|
13
|
+
disable_autosleep: boolean;
|
|
14
|
+
set_proximity_close: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build ADB args for SET_PROPERTY call
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildSetPropertyArgs(pin: string, enabled: boolean): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Parse GET_PROPERTY Bundle output into structured data
|
|
22
|
+
* Input: "Bundle[{disable_guardian=true, set_proximity_close=true, disable_dialogs=true, disable_autosleep=true}]"
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseTestProperties(output: string): TestProperties;
|
|
25
|
+
/**
|
|
26
|
+
* Show current test properties status
|
|
27
|
+
*/
|
|
28
|
+
export declare function stayAwakeStatus(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Manually disable test mode (restore all properties)
|
|
31
|
+
*/
|
|
32
|
+
export declare function stayAwakeDisable(cliPin?: string): Promise<void>;
|
|
6
33
|
/**
|
|
7
34
|
* Child watchdog process - polls for parent death and cleans up
|
|
8
35
|
*/
|
|
9
|
-
export declare function stayAwakeWatchdog(parentPid: number,
|
|
36
|
+
export declare function stayAwakeWatchdog(parentPid: number, pin: string): Promise<void>;
|
|
10
37
|
/**
|
|
11
38
|
* Main stay-awake command handler
|
|
12
39
|
*/
|
|
13
|
-
export declare function stayAwakeCommand(
|
|
40
|
+
export declare function stayAwakeCommand(cliPin?: string, cliIdleTimeout?: number, cliLowBattery?: number, verbose?: boolean): Promise<void>;
|
|
14
41
|
//# sourceMappingURL=stay-awake.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stay-awake.d.ts","sourceRoot":"","sources":["../../src/commands/stay-awake.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"stay-awake.d.ts","sourceRoot":"","sources":["../../src/commands/stay-awake.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,MAAM,WAAW,cAAc;IAC7B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAW5E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAoBlE;AA0CD;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAKrD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKrE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBrF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,CAAC,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,MAAM,EACvB,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,GAAE,OAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAiLf"}
|
|
@@ -1,38 +1,85 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Quest stay-awake command
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Uses Meta Scriptable Testing API (content://com.oculus.rc) to disable
|
|
4
|
+
* autosleep, guardian, and system dialogs for automated testing.
|
|
5
|
+
*
|
|
6
|
+
* Cleanup is critical: with autosleep disabled, the headset drains battery
|
|
7
|
+
* quickly. A watchdog child process ensures cleanup happens even if the
|
|
8
|
+
* parent is killed (TaskStop, terminal close, claude code exit).
|
|
5
9
|
*/
|
|
6
|
-
import { checkADBPath } from '../utils/adb.js';
|
|
7
|
-
import {
|
|
10
|
+
import { checkADBPath, getBatteryInfo, formatBatteryInfo } from '../utils/adb.js';
|
|
11
|
+
import { loadPin, loadConfig } from '../utils/config.js';
|
|
12
|
+
import { execCommand, execCommandFull } from '../utils/exec.js';
|
|
8
13
|
import { execSync, spawn } from 'child_process';
|
|
9
14
|
import * as os from 'os';
|
|
10
15
|
import * as fs from 'fs';
|
|
11
16
|
/**
|
|
12
|
-
*
|
|
17
|
+
* Build ADB args for SET_PROPERTY call
|
|
13
18
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
export function buildSetPropertyArgs(pin, enabled) {
|
|
20
|
+
return [
|
|
21
|
+
'shell', 'content', 'call',
|
|
22
|
+
'--uri', 'content://com.oculus.rc',
|
|
23
|
+
'--method', 'SET_PROPERTY',
|
|
24
|
+
'--extra', `disable_guardian:b:${enabled}`,
|
|
25
|
+
'--extra', `disable_dialogs:b:${enabled}`,
|
|
26
|
+
'--extra', `disable_autosleep:b:${enabled}`,
|
|
27
|
+
'--extra', `set_proximity_close:b:${enabled}`,
|
|
28
|
+
'--extra', `PIN:s:${pin}`,
|
|
29
|
+
];
|
|
17
30
|
}
|
|
18
31
|
/**
|
|
19
|
-
*
|
|
32
|
+
* Parse GET_PROPERTY Bundle output into structured data
|
|
33
|
+
* Input: "Bundle[{disable_guardian=true, set_proximity_close=true, disable_dialogs=true, disable_autosleep=true}]"
|
|
20
34
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
35
|
+
export function parseTestProperties(output) {
|
|
36
|
+
const defaults = {
|
|
37
|
+
disable_guardian: false,
|
|
38
|
+
disable_dialogs: false,
|
|
39
|
+
disable_autosleep: false,
|
|
40
|
+
set_proximity_close: false,
|
|
41
|
+
};
|
|
42
|
+
const match = output.match(/Bundle\[\{(.+)\}\]/);
|
|
43
|
+
if (!match)
|
|
44
|
+
return defaults;
|
|
45
|
+
const pairs = match[1].split(',').map(s => s.trim());
|
|
46
|
+
for (const pair of pairs) {
|
|
47
|
+
const [key, value] = pair.split('=');
|
|
48
|
+
if (key && value && key in defaults) {
|
|
49
|
+
defaults[key] = value === 'true';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return defaults;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Call SET_PROPERTY to enable or disable test mode
|
|
56
|
+
*/
|
|
57
|
+
async function setTestProperties(pin, enabled) {
|
|
58
|
+
const args = buildSetPropertyArgs(pin, enabled);
|
|
59
|
+
await execCommand('adb', args);
|
|
23
60
|
}
|
|
24
61
|
/**
|
|
25
|
-
*
|
|
62
|
+
* Call GET_PROPERTY and return parsed test properties
|
|
26
63
|
*/
|
|
27
|
-
async function
|
|
28
|
-
await
|
|
64
|
+
async function getTestProperties() {
|
|
65
|
+
const result = await execCommandFull('adb', [
|
|
66
|
+
'shell', 'content', 'call',
|
|
67
|
+
'--uri', 'content://com.oculus.rc',
|
|
68
|
+
'--method', 'GET_PROPERTY',
|
|
69
|
+
]);
|
|
70
|
+
return parseTestProperties(result.stdout);
|
|
29
71
|
}
|
|
30
72
|
/**
|
|
31
|
-
*
|
|
32
|
-
* Note: automation_disable actually RE-ENABLES normal proximity sensor automation
|
|
73
|
+
* Format test properties for display
|
|
33
74
|
*/
|
|
34
|
-
|
|
35
|
-
|
|
75
|
+
function formatTestProperties(props) {
|
|
76
|
+
const lines = [
|
|
77
|
+
` Guardian disabled: ${props.disable_guardian}`,
|
|
78
|
+
` Dialogs disabled: ${props.disable_dialogs}`,
|
|
79
|
+
` Autosleep disabled: ${props.disable_autosleep}`,
|
|
80
|
+
` Proximity close: ${props.set_proximity_close}`,
|
|
81
|
+
];
|
|
82
|
+
return lines.join('\n');
|
|
36
83
|
}
|
|
37
84
|
/**
|
|
38
85
|
* Wake the Quest screen
|
|
@@ -40,33 +87,45 @@ async function enableProximitySensor() {
|
|
|
40
87
|
async function wakeScreen() {
|
|
41
88
|
await execCommand('adb', ['shell', 'input', 'keyevent', 'KEYCODE_WAKEUP']);
|
|
42
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Show current test properties status
|
|
92
|
+
*/
|
|
93
|
+
export async function stayAwakeStatus() {
|
|
94
|
+
checkADBPath();
|
|
95
|
+
const props = await getTestProperties();
|
|
96
|
+
console.log('Scriptable Testing properties:');
|
|
97
|
+
console.log(formatTestProperties(props));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Manually disable test mode (restore all properties)
|
|
101
|
+
*/
|
|
102
|
+
export async function stayAwakeDisable(cliPin) {
|
|
103
|
+
checkADBPath();
|
|
104
|
+
const pin = loadPin(cliPin);
|
|
105
|
+
await setTestProperties(pin, false);
|
|
106
|
+
console.log('Test mode disabled — guardian, dialogs, and autosleep restored');
|
|
107
|
+
}
|
|
43
108
|
/**
|
|
44
109
|
* Child watchdog process - polls for parent death and cleans up
|
|
45
110
|
*/
|
|
46
|
-
export async function stayAwakeWatchdog(parentPid,
|
|
47
|
-
const pollInterval = 5000;
|
|
111
|
+
export async function stayAwakeWatchdog(parentPid, pin) {
|
|
112
|
+
const pollInterval = 5000;
|
|
48
113
|
const checkParent = setInterval(() => {
|
|
49
114
|
try {
|
|
50
|
-
// Check if parent process still exists
|
|
51
115
|
process.kill(parentPid, 0);
|
|
52
|
-
// Parent still alive, continue polling
|
|
53
116
|
}
|
|
54
117
|
catch {
|
|
55
|
-
// Parent is dead - perform cleanup
|
|
56
118
|
console.log('Parent process died, restoring Quest settings...');
|
|
57
119
|
clearInterval(checkParent);
|
|
58
|
-
// Restore settings synchronously
|
|
59
120
|
try {
|
|
60
|
-
|
|
61
|
-
execSync(`adb
|
|
62
|
-
// Cleanup PID file
|
|
121
|
+
const args = buildSetPropertyArgs(pin, false);
|
|
122
|
+
execSync(`adb ${args.join(' ')}`, { stdio: 'ignore' });
|
|
63
123
|
const pidFile = `${os.homedir()}/.quest-dev-stay-awake.pid`;
|
|
64
124
|
try {
|
|
65
125
|
fs.unlinkSync(pidFile);
|
|
66
126
|
}
|
|
67
127
|
catch { }
|
|
68
|
-
console.log(
|
|
69
|
-
console.log('Proximity sensor re-enabled');
|
|
128
|
+
console.log('Test mode disabled — guardian, dialogs, and autosleep restored');
|
|
70
129
|
}
|
|
71
130
|
catch (err) {
|
|
72
131
|
console.error('Failed to restore settings:', err.message);
|
|
@@ -78,10 +137,9 @@ export async function stayAwakeWatchdog(parentPid, originalTimeout) {
|
|
|
78
137
|
/**
|
|
79
138
|
* Main stay-awake command handler
|
|
80
139
|
*/
|
|
81
|
-
export async function stayAwakeCommand(
|
|
82
|
-
// Check prerequisites
|
|
140
|
+
export async function stayAwakeCommand(cliPin, cliIdleTimeout, cliLowBattery, verbose = false) {
|
|
83
141
|
checkADBPath();
|
|
84
|
-
// Check devices
|
|
142
|
+
// Check devices
|
|
85
143
|
try {
|
|
86
144
|
const output = await execCommand('adb', ['devices']);
|
|
87
145
|
const lines = output.trim().split('\n').slice(1);
|
|
@@ -95,77 +153,82 @@ export async function stayAwakeCommand(idleTimeout = 300000) {
|
|
|
95
153
|
console.error('Error: Failed to list ADB devices');
|
|
96
154
|
process.exit(1);
|
|
97
155
|
}
|
|
156
|
+
const config = loadConfig();
|
|
157
|
+
const pin = loadPin(cliPin);
|
|
158
|
+
const idleTimeout = cliIdleTimeout ?? config.idleTimeout ?? 300000;
|
|
159
|
+
const lowBattery = cliLowBattery ?? config.lowBattery ?? 10;
|
|
98
160
|
// PID file management
|
|
99
161
|
const pidFilePath = `${os.homedir()}/.quest-dev-stay-awake.pid`;
|
|
100
|
-
// Check for existing process
|
|
101
162
|
if (fs.existsSync(pidFilePath)) {
|
|
102
163
|
const existingPid = parseInt(fs.readFileSync(pidFilePath, 'utf-8'));
|
|
103
164
|
try {
|
|
104
|
-
process.kill(existingPid, 0);
|
|
165
|
+
process.kill(existingPid, 0);
|
|
105
166
|
console.error(`Error: stay-awake is already running (PID: ${existingPid})`);
|
|
106
167
|
process.exit(1);
|
|
107
168
|
}
|
|
108
169
|
catch {
|
|
109
|
-
// Process dead, cleanup stale PID file
|
|
110
170
|
fs.unlinkSync(pidFilePath);
|
|
111
171
|
}
|
|
112
172
|
}
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
console.log(`Original screen timeout: ${originalTimeout}ms (${Math.round(originalTimeout / 1000)}s)`);
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
console.error('Failed to get current screen timeout');
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
173
|
+
// Show current state
|
|
174
|
+
const beforeProps = await getTestProperties();
|
|
175
|
+
console.log('Current test properties:');
|
|
176
|
+
console.log(formatTestProperties(beforeProps));
|
|
123
177
|
// Write PID file
|
|
124
178
|
try {
|
|
125
179
|
fs.writeFileSync(pidFilePath, process.pid.toString());
|
|
126
180
|
}
|
|
127
181
|
catch (error) {
|
|
128
|
-
console.warn('Failed to write PID file
|
|
182
|
+
console.warn('Failed to write PID file');
|
|
129
183
|
}
|
|
130
|
-
// Spawn child
|
|
184
|
+
// Spawn watchdog child process
|
|
131
185
|
let childProcess = null;
|
|
132
186
|
try {
|
|
133
187
|
childProcess = spawn(process.execPath, [
|
|
134
|
-
process.argv[1],
|
|
188
|
+
process.argv[1],
|
|
135
189
|
'stay-awake-watchdog',
|
|
136
190
|
'--parent-pid', process.pid.toString(),
|
|
137
|
-
'--
|
|
191
|
+
'--pin', pin,
|
|
138
192
|
], {
|
|
139
193
|
detached: true,
|
|
140
|
-
stdio: 'ignore'
|
|
194
|
+
stdio: 'ignore',
|
|
141
195
|
});
|
|
142
|
-
childProcess.unref();
|
|
196
|
+
childProcess.unref();
|
|
143
197
|
}
|
|
144
198
|
catch (error) {
|
|
145
199
|
console.warn('Failed to spawn watchdog child process');
|
|
146
200
|
}
|
|
147
|
-
//
|
|
201
|
+
// Enable test mode
|
|
202
|
+
try {
|
|
203
|
+
await setTestProperties(pin, true);
|
|
204
|
+
console.log('Test mode enabled — guardian, dialogs, and autosleep disabled');
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
console.error('Failed to enable test mode:', error.message);
|
|
208
|
+
console.error('Requires Quest OS v44+ and a valid Meta Store PIN.');
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
// Wake screen
|
|
148
212
|
try {
|
|
149
213
|
await wakeScreen();
|
|
150
214
|
console.log('Quest screen woken up');
|
|
151
|
-
await disableProximitySensor();
|
|
152
|
-
console.log('Proximity sensor disabled');
|
|
153
215
|
}
|
|
154
216
|
catch (error) {
|
|
155
|
-
console.error('Failed to wake screen
|
|
217
|
+
console.error('Failed to wake screen:', error.message);
|
|
156
218
|
}
|
|
157
|
-
//
|
|
158
|
-
|
|
219
|
+
// Battery monitoring state
|
|
220
|
+
let lastReportedBucket = -1; // Track 5% boundary crossings
|
|
221
|
+
// Initial battery check
|
|
159
222
|
try {
|
|
160
|
-
await
|
|
161
|
-
console.log(`
|
|
162
|
-
|
|
223
|
+
const battery = await getBatteryInfo();
|
|
224
|
+
console.log(`Battery: ${formatBatteryInfo(battery)}`);
|
|
225
|
+
lastReportedBucket = Math.floor(battery.level / 5) * 5;
|
|
163
226
|
}
|
|
164
227
|
catch (error) {
|
|
165
|
-
console.
|
|
166
|
-
process.exit(1);
|
|
228
|
+
console.warn('Failed to read battery status');
|
|
167
229
|
}
|
|
168
|
-
|
|
230
|
+
console.log(`Quest will stay awake (idle timeout: ${Math.round(idleTimeout / 1000)}s, low battery exit: ${lowBattery}%). Press Ctrl-C to restore.`);
|
|
231
|
+
// Idle timer
|
|
169
232
|
let idleTimerHandle = null;
|
|
170
233
|
let cleanupInProgress = false;
|
|
171
234
|
const resetIdleTimer = () => {
|
|
@@ -176,58 +239,72 @@ export async function stayAwakeCommand(idleTimeout = 300000) {
|
|
|
176
239
|
cleanup();
|
|
177
240
|
}, idleTimeout);
|
|
178
241
|
};
|
|
179
|
-
//
|
|
242
|
+
// Cleanup handler
|
|
180
243
|
const cleanup = () => {
|
|
181
244
|
if (cleanupInProgress)
|
|
182
|
-
return;
|
|
245
|
+
return;
|
|
183
246
|
cleanupInProgress = true;
|
|
184
|
-
// Clear idle timer
|
|
185
247
|
if (idleTimerHandle)
|
|
186
248
|
clearTimeout(idleTimerHandle);
|
|
187
|
-
|
|
249
|
+
if (batteryInterval)
|
|
250
|
+
clearInterval(batteryInterval);
|
|
188
251
|
if (childProcess) {
|
|
189
252
|
try {
|
|
190
253
|
childProcess.kill();
|
|
191
254
|
}
|
|
192
255
|
catch { }
|
|
193
256
|
}
|
|
194
|
-
console.log('\nRestoring
|
|
257
|
+
console.log('\nRestoring settings...');
|
|
195
258
|
try {
|
|
196
|
-
// Remove PID file
|
|
197
259
|
try {
|
|
198
260
|
fs.unlinkSync(pidFilePath);
|
|
199
261
|
}
|
|
200
262
|
catch { }
|
|
201
|
-
|
|
202
|
-
execSync(`adb
|
|
203
|
-
|
|
204
|
-
console.log(`Screen timeout restored to ${originalTimeout}ms (${Math.round(originalTimeout / 1000)}s)`);
|
|
205
|
-
console.log(`Proximity sensor re-enabled`);
|
|
263
|
+
const args = buildSetPropertyArgs(pin, false);
|
|
264
|
+
execSync(`adb ${args.join(' ')}`, { stdio: 'ignore' });
|
|
265
|
+
console.log('Test mode disabled — guardian, dialogs, and autosleep restored');
|
|
206
266
|
}
|
|
207
267
|
catch (error) {
|
|
208
268
|
console.error('Failed to restore settings:', error.message);
|
|
209
269
|
}
|
|
210
270
|
process.exit(0);
|
|
211
271
|
};
|
|
212
|
-
//
|
|
272
|
+
// Signal handlers
|
|
213
273
|
process.on('SIGINT', cleanup);
|
|
214
274
|
process.on('SIGTERM', cleanup);
|
|
215
275
|
process.on('SIGHUP', cleanup);
|
|
216
|
-
//
|
|
276
|
+
// Activity reset via SIGUSR1
|
|
217
277
|
process.on('SIGUSR1', () => {
|
|
218
|
-
|
|
278
|
+
const now = new Date().toLocaleTimeString();
|
|
279
|
+
console.log(`[${now}] Activity detected, resetting idle timer`);
|
|
219
280
|
resetIdleTimer();
|
|
220
281
|
});
|
|
221
282
|
// Start idle timer
|
|
222
283
|
resetIdleTimer();
|
|
223
|
-
//
|
|
284
|
+
// Battery monitoring loop (every 60s)
|
|
285
|
+
const batteryInterval = setInterval(async () => {
|
|
286
|
+
try {
|
|
287
|
+
const battery = await getBatteryInfo();
|
|
288
|
+
const currentBucket = Math.floor(battery.level / 5) * 5;
|
|
289
|
+
if (verbose) {
|
|
290
|
+
console.log(`Battery: ${formatBatteryInfo(battery)}`);
|
|
291
|
+
}
|
|
292
|
+
else if (currentBucket !== lastReportedBucket) {
|
|
293
|
+
console.log(`Battery: ${formatBatteryInfo(battery)}`);
|
|
294
|
+
}
|
|
295
|
+
lastReportedBucket = currentBucket;
|
|
296
|
+
if (battery.level <= lowBattery && battery.state === 'not charging') {
|
|
297
|
+
console.log(`\nBattery critically low (${battery.level}%), exiting to preserve battery...`);
|
|
298
|
+
cleanup();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// Ignore battery check failures (device might be briefly unavailable)
|
|
303
|
+
}
|
|
304
|
+
}, 60000);
|
|
305
|
+
// Keep process alive
|
|
224
306
|
console.log('Keeping Quest awake...');
|
|
225
|
-
setInterval(() => {
|
|
226
|
-
// Do nothing, just keep process alive
|
|
227
|
-
}, 60000); // Check every minute
|
|
228
|
-
// Prevent process from exiting
|
|
229
307
|
await new Promise((resolve) => {
|
|
230
|
-
// This will only resolve when cleanup is called
|
|
231
308
|
process.on('exit', () => resolve());
|
|
232
309
|
});
|
|
233
310
|
}
|