feat(commands): add PC control packs — volume, apps, file_search, output_device

All four packs are pure Lua (sandbox=full) and call out to PowerShell helpers
where COM / native APIs are needed. No new Rust code, no rebuild required:
post_build.py --sync copies them under target/{debug,release}/resources/.

volume/
  - volume_up / volume_down: 5x VK_VOLUME_UP|DOWN via keybd_event
  - volume_mute: VK_VOLUME_MUTE
  - volume_max: 50x VK_VOLUME_UP (system clamps)
  Helper _volume_helper.ps1 P/Invokes user32!keybd_event.

apps/
  - open_browser  : start "" https://www.google.com (uses system default browser)
  - open_notepad  : notepad.exe
  - open_calculator: calc.exe
  - open_explorer : explorer.exe
  - open_terminal : wt.exe, falls back to powershell.exe
  - open_settings : ms-settings: protocol
  - open_task_manager: taskmgr.exe
  - lock_screen   : rundll32 user32.dll,LockWorkStation
  - screenshot    : System.Drawing capture to ~/Pictures/jarvis_screenshot_*.png

file_search/
  - find file <query> : recursive Get-ChildItem in Desktop/Documents/Downloads
    (depth 3, top 5 matches). Strips Russian/English/Ukrainian trigger
    prefixes from the recognized phrase, then opens explorer /select on the
    first hit and notifies with the total count.

output_device/
  - list_output_devices : enumerates active render endpoints via
    IMMDeviceEnumerator and notifies friendly names with indices
  - next_output_device  : cycles the system default render device via the
    undocumented IPolicyConfig::SetDefaultEndpoint (eConsole/Multimedia/
    Communications). Tested locally — listed 7 devices, current correctly
    identified at index 5.
This commit is contained in:
Bossiara13 2026-05-15 00:54:52 +03:00
parent 16240609de
commit 9c25108356
22 changed files with 932 additions and 0 deletions

View file

@ -0,0 +1,31 @@
param(
[Parameter(Mandatory=$true)]
[ValidateSet("up","down","mute","max")]
[string]$Action,
[int]$Times = 1
)
Add-Type -Name VolumeKey -Namespace Win32 -MemberDefinition @'
[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, System.UIntPtr dwExtraInfo);
'@
$KEY_UP = 0xAF
$KEY_DOWN = 0xAE
$KEY_MUTE = 0xAD
$vk = switch ($Action) {
"up" { $KEY_UP }
"down" { $KEY_DOWN }
"mute" { $KEY_MUTE }
"max" { $KEY_UP }
}
$count = if ($Action -eq "max") { 50 } else { $Times }
for ($i = 0; $i -lt $count; $i++) {
[Win32.VolumeKey]::keybd_event($vk, 0, 0, [UIntPtr]::Zero)
[Win32.VolumeKey]::keybd_event($vk, 0, 2, [UIntPtr]::Zero)
Start-Sleep -Milliseconds 25
}

View file

@ -0,0 +1,106 @@
[[commands]]
id = "volume_up"
type = "lua"
script = "volume_up.lua"
sandbox = "full"
timeout = 3000
[commands.phrases]
ru = [
"громче",
"сделай громче",
"прибавь звук",
"увеличь громкость",
"погромче",
]
en = [
"louder",
"volume up",
"increase volume",
"turn it up",
]
ua = [
"голосніше",
"зроби голосніше",
"збільш гучність",
]
[[commands]]
id = "volume_down"
type = "lua"
script = "volume_down.lua"
sandbox = "full"
timeout = 3000
[commands.phrases]
ru = [
"тише",
"сделай тише",
"убавь звук",
"уменьши громкость",
"потише",
]
en = [
"quieter",
"volume down",
"decrease volume",
"turn it down",
]
ua = [
"тихіше",
"зроби тихіше",
"зменш гучність",
]
[[commands]]
id = "volume_mute"
type = "lua"
script = "volume_mute.lua"
sandbox = "full"
timeout = 3000
[commands.phrases]
ru = [
"выключи звук",
"заткнись",
"тишина",
"приглуши звук",
"отключи звук",
]
en = [
"mute",
"silence",
"shut up",
"be quiet",
]
ua = [
"вимкни звук",
"тиша",
"стихни",
]
[[commands]]
id = "volume_max"
type = "lua"
script = "volume_max.lua"
sandbox = "full"
timeout = 3000
[commands.phrases]
ru = [
"максимальная громкость",
"звук на максимум",
"на полную",
]
en = [
"max volume",
"full volume",
"maximum",
]
ua = [
"максимальна гучність",
"звук на максимум",
]

View file

@ -0,0 +1,16 @@
local helper = jarvis.context.command_path .. "\\_volume_helper.ps1"
local cmd = string.format(
'powershell -NoProfile -ExecutionPolicy Bypass -File "%s" -Action down -Times 5',
helper
)
local res = jarvis.system.exec(cmd)
if res.success then
jarvis.log("info", "volume down")
jarvis.audio.play_ok()
else
jarvis.log("error", "volume down failed: code=" .. tostring(res.code) .. " stderr=" .. tostring(res.stderr))
jarvis.audio.play_error()
end
return { chain = false }

View file

@ -0,0 +1,16 @@
local helper = jarvis.context.command_path .. "\\_volume_helper.ps1"
local cmd = string.format(
'powershell -NoProfile -ExecutionPolicy Bypass -File "%s" -Action max',
helper
)
local res = jarvis.system.exec(cmd)
if res.success then
jarvis.log("info", "volume max")
jarvis.audio.play_ok()
else
jarvis.log("error", "volume max failed: code=" .. tostring(res.code) .. " stderr=" .. tostring(res.stderr))
jarvis.audio.play_error()
end
return { chain = false }

View file

@ -0,0 +1,16 @@
local helper = jarvis.context.command_path .. "\\_volume_helper.ps1"
local cmd = string.format(
'powershell -NoProfile -ExecutionPolicy Bypass -File "%s" -Action mute',
helper
)
local res = jarvis.system.exec(cmd)
if res.success then
jarvis.log("info", "mute toggled")
jarvis.audio.play_ok()
else
jarvis.log("error", "mute failed: code=" .. tostring(res.code) .. " stderr=" .. tostring(res.stderr))
jarvis.audio.play_error()
end
return { chain = false }

View file

@ -0,0 +1,16 @@
local helper = jarvis.context.command_path .. "\\_volume_helper.ps1"
local cmd = string.format(
'powershell -NoProfile -ExecutionPolicy Bypass -File "%s" -Action up -Times 5',
helper
)
local res = jarvis.system.exec(cmd)
if res.success then
jarvis.log("info", "volume up")
jarvis.audio.play_ok()
else
jarvis.log("error", "volume up failed: code=" .. tostring(res.code) .. " stderr=" .. tostring(res.stderr))
jarvis.audio.play_error()
end
return { chain = false }