"""JARVIS System Control — full laptop/phone control like Siri.""" import subprocess import os import platform from tools import tool # ─── Volume ────────────────────────────────────── @tool( name="set_volume", description="Set system volume (0-100) or mute/unmute", parameters={ "type": "object", "properties": { "level": {"type": "integer", "description": "Volume 0-100, or -1 to mute, -2 to unmute"}, }, "required": ["level"], }, ) def set_volume(level: int) -> str: if level == -1: subprocess.run(["osascript", "-e", "set volume with output muted"]) return "Muted" elif level == -2: subprocess.run(["osascript", "-e", "set volume without output muted"]) return "Unmuted" else: level = max(0, min(100, level)) subprocess.run(["osascript", "-e", f"set volume output volume {level}"]) return f"Volume set to {level}%" @tool( name="get_volume", description="Get current system volume level", parameters={"type": "object", "properties": {}}, ) def get_volume() -> str: result = subprocess.run( ["osascript", "-e", "output volume of (get volume settings)"], capture_output=True, text=True, ) return f"Volume: {result.stdout.strip()}%" # ─── Brightness ────────────────────────────────── @tool( name="set_brightness", description="Set screen brightness (0.0 to 1.0)", parameters={ "type": "object", "properties": { "level": {"type": "number", "description": "Brightness 0.0-1.0"}, }, "required": ["level"], }, ) def set_brightness(level: float) -> str: level = max(0.0, min(1.0, level)) # Use brightness command if available try: subprocess.run(["brightness", str(level)], capture_output=True, timeout=5) return f"Brightness set to {int(level*100)}%" except Exception: subprocess.run([ "osascript", "-e", f''' tell application "System Preferences" reveal anchor "displaysDisplayTab" of pane id "com.apple.preference.displays" end tell ''' ], stderr=subprocess.DEVNULL) return f"Brightness control requires 'brightness' CLI. Install: brew install brightness" # ─── Dark Mode ─────────────────────────────────── @tool( name="toggle_dark_mode", description="Toggle dark mode on/off", parameters={ "type": "object", "properties": { "enable": {"type": "boolean", "description": "True for dark, False for light"}, }, "required": ["enable"], }, ) def toggle_dark_mode(enable: bool) -> str: mode = "true" if enable else "false" subprocess.run([ "osascript", "-e", f'tell application "System Events" to tell appearance preferences to set dark mode to {mode}' ]) return f"Dark mode {'enabled' if enable else 'disabled'}" # ─── WiFi ──────────────────────────────────────── @tool( name="wifi_control", description="Turn WiFi on/off or get status", parameters={ "type": "object", "properties": { "action": {"type": "string", "description": "'on', 'off', or 'status'"}, }, "required": ["action"], }, ) def wifi_control(action: str) -> str: if action == "on": subprocess.run(["networksetup", "-setairportpower", "en0", "on"]) return "WiFi turned on" elif action == "off": subprocess.run(["networksetup", "-setairportpower", "en0", "off"]) return "WiFi turned off" else: result = subprocess.run( ["networksetup", "-getairportnetwork", "en0"], capture_output=True, text=True, ) return result.stdout.strip() # ─── Bluetooth ─────────────────────────────────── @tool( name="bluetooth_control", description="Turn Bluetooth on/off", parameters={ "type": "object", "properties": { "action": {"type": "string", "description": "'on' or 'off'"}, }, "required": ["action"], }, ) def bluetooth_control(action: str) -> str: try: if action == "on": subprocess.run(["blueutil", "--power", "1"], capture_output=True, timeout=5) return "Bluetooth on" else: subprocess.run(["blueutil", "--power", "0"], capture_output=True, timeout=5) return "Bluetooth off" except Exception: return "Install blueutil: brew install blueutil" # ─── Do Not Disturb ────────────────────────────── @tool( name="do_not_disturb", description="Toggle Do Not Disturb / Focus mode", parameters={ "type": "object", "properties": { "enable": {"type": "boolean", "description": "True to enable DND"}, }, "required": ["enable"], }, ) def do_not_disturb(enable: bool) -> str: if enable: subprocess.run([ "osascript", "-e", ''' tell application "System Events" tell process "ControlCenter" -- Toggle Focus via menu bar end tell end tell ''' ], stderr=subprocess.DEVNULL) # Alternative: use shortcuts subprocess.run(["shortcuts", "run", "Focus On"], capture_output=True, timeout=5) return "Do Not Disturb enabled" else: subprocess.run(["shortcuts", "run", "Focus Off"], capture_output=True, timeout=5) return "Do Not Disturb disabled" # ─── Screenshots ───────────────────────────────── @tool( name="screenshot", description="Take a screenshot", parameters={ "type": "object", "properties": { "area": {"type": "string", "description": "'full' for fullscreen, 'select' for selection"}, }, "required": [], }, ) def screenshot(area: str = "full") -> str: path = os.path.expanduser(f"~/Desktop/jarvis_screenshot_{int(__import__('time').time())}.png") if area == "select": subprocess.run(["screencapture", "-i", path]) else: subprocess.run(["screencapture", path]) return f"Screenshot saved: {path}" # ─── Music / Media Control ─────────────────────── @tool( name="media_control", description="Control music/media playback (play, pause, next, previous, now playing)", parameters={ "type": "object", "properties": { "action": {"type": "string", "description": "'play', 'pause', 'next', 'previous', 'now_playing'"}, }, "required": ["action"], }, ) def media_control(action: str) -> str: if action == "now_playing": result = subprocess.run([ "osascript", "-e", ''' try tell application "Music" set trackName to name of current track set artistName to artist of current track return trackName & " by " & artistName end tell on error try tell application "Spotify" return name of current track & " by " & artist of current track end tell on error return "Nothing playing" end try end try ''' ], capture_output=True, text=True, timeout=5) return result.stdout.strip() key_map = { "play": "play", "pause": "pause", "next": "next track", "previous": "previous track", } cmd = key_map.get(action, "play") # Try Music app first, then Spotify subprocess.run([ "osascript", "-e", f''' try tell application "Music" to {cmd} on error try tell application "Spotify" to {cmd} end try end try ''' ], capture_output=True, timeout=5) return f"Media: {action}" # ─── Clipboard ─────────────────────────────────── @tool( name="clipboard", description="Read or write the clipboard", parameters={ "type": "object", "properties": { "action": {"type": "string", "description": "'read' to get clipboard, 'write' to set it"}, "text": {"type": "string", "description": "Text to write (only for 'write')"}, }, "required": ["action"], }, ) def clipboard(action: str, text: str = "") -> str: if action == "read": result = subprocess.run(["pbpaste"], capture_output=True, text=True) return f"Clipboard: {result.stdout[:500]}" elif action == "write": proc = subprocess.Popen(["pbcopy"], stdin=subprocess.PIPE) proc.communicate(text.encode()) return f"Copied to clipboard" return "Unknown action" # ─── Notifications / Alerts ────────────────────── @tool( name="send_notification", description="Send a macOS notification/alert", parameters={ "type": "object", "properties": { "title": {"type": "string", "description": "Notification title"}, "message": {"type": "string", "description": "Notification body"}, }, "required": ["title", "message"], }, ) def send_notification(title: str, message: str) -> str: safe_title = title.replace('\\', '').replace('"', '').replace("'", '')[:80] safe_msg = message.replace('\\', '').replace('"', '').replace("'", '')[:120] subprocess.run([ "osascript", "-e", f'display notification "{safe_msg}" with title "{safe_title}" sound name "default"' ]) return f"Notification sent: {title}" # ─── App Management ────────────────────────────── @tool( name="list_running_apps", description="List all running applications", parameters={"type": "object", "properties": {}}, ) def list_running_apps() -> str: result = subprocess.run([ "osascript", "-e", 'tell application "System Events" to get name of every process whose background only is false' ], capture_output=True, text=True, timeout=5) return f"Running apps: {result.stdout.strip()}" @tool( name="quit_app", description="Quit/close an application", parameters={ "type": "object", "properties": { "app_name": {"type": "string", "description": "App name to quit"}, }, "required": ["app_name"], }, ) def quit_app(app_name: str) -> str: safe_name = app_name.replace('\\', '').replace('"', '').replace("'", '').replace('`', '') subprocess.run([ "osascript", "-e", f'tell application "{safe_name}" to quit' ], capture_output=True, timeout=5) return f"Quit {app_name}" # ─── Timer / Alarm ─────────────────────────────── @tool( name="set_timer", description="Set a timer that alerts after N seconds/minutes", parameters={ "type": "object", "properties": { "seconds": {"type": "integer", "description": "Timer duration in seconds"}, "label": {"type": "string", "description": "What the timer is for"}, }, "required": ["seconds"], }, ) def set_timer(seconds: int, label: str = "Timer") -> str: import threading def timer_alert(): import time time.sleep(seconds) subprocess.run([ "osascript", "-e", f'display dialog "Timer: {label}" with title "JARVIS Timer" buttons {{"OK"}} default button "OK" giving up after 30' ]) subprocess.run(["say", "-v", "Daniel", f"Sir, your {label} timer is done."]) threading.Thread(target=timer_alert, daemon=True).start() mins = seconds // 60 secs = seconds % 60 if mins: return f"Timer set: {label} — {mins}m {secs}s" return f"Timer set: {label} — {secs}s" # ─── Lock Screen / Sleep ───────────────────────── @tool( name="lock_screen", description="Lock the screen", parameters={"type": "object", "properties": {}}, ) def lock_screen() -> str: subprocess.run([ "osascript", "-e", 'tell application "System Events" to keystroke "q" using {control down, command down}' ]) return "Screen locked" @tool( name="sleep_display", description="Put the display to sleep", parameters={"type": "object", "properties": {}}, ) def sleep_display() -> str: subprocess.run(["pmset", "displaysleepnow"]) return "Display sleeping" # ─── Trash ─────────────────────────────────────── @tool( name="empty_trash", description="Empty the trash", parameters={"type": "object", "properties": {}}, ) def empty_trash() -> str: subprocess.run([ "osascript", "-e", 'tell application "Finder" to empty trash' ], capture_output=True, timeout=10) return "Trash emptied" # ─── Text to Speech (announce) ─────────────────── @tool( name="announce", description="Speak a message out loud through the speakers", parameters={ "type": "object", "properties": { "message": {"type": "string", "description": "Message to speak"}, }, "required": ["message"], }, ) def announce(message: str) -> str: subprocess.run(["say", "-v", "Daniel", "-r", "180", message]) return f"Announced: {message}" # ─── Open URL ──────────────────────────────────── @tool( name="open_url", description="Open a URL in the default browser", parameters={ "type": "object", "properties": { "url": {"type": "string", "description": "URL to open"}, }, "required": ["url"], }, ) def open_url(url: str) -> str: subprocess.run(["open", url]) return f"Opened: {url}" # ─── Spotlight Search ──────────────────────────── @tool( name="search_files", description="Search for files on the system using Spotlight", parameters={ "type": "object", "properties": { "query": {"type": "string", "description": "Search query"}, }, "required": ["query"], }, ) def search_files(query: str) -> str: result = subprocess.run( ["mdfind", query, "-limit", "10"], capture_output=True, text=True, timeout=10, ) files = result.stdout.strip().split("\n")[:10] return "\n".join(files) if files[0] else "No files found" # ─── Create Reminder ───────────────────────────── @tool( name="create_reminder", description="Create a reminder in the Reminders app", parameters={ "type": "object", "properties": { "title": {"type": "string", "description": "Reminder text"}, }, "required": ["title"], }, ) def create_reminder(title: str) -> str: subprocess.run([ "osascript", "-e", f''' tell application "Reminders" make new reminder with properties {{name:"{title}"}} end tell ''' ], capture_output=True, timeout=5) return f"Reminder created: {title}" # ─── Calendar ──────────────────────────────────── @tool( name="get_events", description="Get today's calendar events", parameters={"type": "object", "properties": {}}, ) def get_events() -> str: result = subprocess.run([ "osascript", "-e", ''' set today to current date set time of today to 0 set tomorrow to today + 86400 tell application "Calendar" set output to "" repeat with cal in calendars set evts to (every event of cal whose start date >= today and start date < tomorrow) repeat with evt in evts set output to output & summary of evt & " at " & time string of start date of evt & "\\n" end repeat end repeat if output is "" then return "No events today" end if return output end tell ''' ], capture_output=True, text=True, timeout=10) return result.stdout.strip()