# API Examples

Code examples for common automation tasks

## Python Examples

### Basic Connection

    import json
    import websocket

    ws = websocket.create_connection("ws://localhost:8777/ws")

    def send_command(command, **params):
        request = {"command": command, **params}
        ws.send(json.dumps(request))
        return json.loads(ws.recv())

    # List all clients
    result = send_command("get_clients")
    for client in result["clients"]:
        print(f"{client['title']} ({client['class']})")

    ws.close()

### Using the Client Library

    from dwn_api_client import DWNClient

    client = DWNClient()
    client.connect()

    # Get all clients
    clients = client.get_clients()
    print(f"Found {len(clients)} clients")

    # Focus client by title
    for c in clients:
        if "Firefox" in c["title"]:
            client.focus_client(c["window"])
            break

    # Switch to workspace 3
    client.switch_workspace(2)

    # Type some text
    client.key_type("Hello from Python!")

    client.disconnect()

### Screenshot and OCR

    import base64
    from dwn_api_client import DWNClient

    client = DWNClient()
    client.connect()

    # Take fullscreen screenshot
    result = client.screenshot("fullscreen")

    # Save to file
    png_data = base64.b64decode(result["data"])
    with open("screenshot.png", "wb") as f:
        f.write(png_data)

    print(f"Saved {result['width']}x{result['height']} screenshot")

    # Extract text with OCR
    ocr_result = client.ocr(result["data"])
    print(f"Extracted text (confidence: {ocr_result['confidence']:.0%}):")
    print(ocr_result["text"])

    client.disconnect()

### Client Arrangement Script

    from dwn_api_client import DWNClient

    def arrange_coding_setup(client):
        """Arrange clients for coding: editor left, terminal right"""
        clients = client.get_clients()

        # Find VS Code and terminal
        vscode = None
        terminal = None
        for c in clients:
            if "code" in c["class"].lower():
                vscode = c
            elif "terminal" in c["class"].lower():
                terminal = c

        if vscode:
            client.move_client(vscode["window"], 0, 32)
            client.resize_client(vscode["window"], 960, 1048)

        if terminal:
            client.move_client(terminal["window"], 960, 32)
            client.resize_client(terminal["window"], 960, 1048)

    client = DWNClient()
    client.connect()
    arrange_coding_setup(client)
    client.disconnect()

### Async Client

    import asyncio
    import json
    import websockets

    async def main():
        async with websockets.connect("ws://localhost:8777/ws") as ws:
            # Send command
            await ws.send(json.dumps({"command": "get_clients"}))

            # Receive response
            response = json.loads(await ws.recv())

            for client in response["clients"]:
                print(f"Client: {client['title']}")

    asyncio.run(main())

## JavaScript Examples

### Browser WebSocket

    class DWNClient {
        constructor(url = 'ws://localhost:8777/ws') {
            this.url = url;
            this.ws = null;
            this.pending = new Map();
            this.requestId = 0;
        }

        connect() {
            return new Promise((resolve, reject) => {
                this.ws = new WebSocket(this.url);
                this.ws.onopen = () => resolve();
                this.ws.onerror = (e) => reject(e);
                this.ws.onmessage = (e) => this.handleMessage(e);
            });
        }

        handleMessage(event) {
            const response = JSON.parse(event.data);
            // Handle response
            console.log('Received:', response);
        }

        send(command, params = {}) {
            const request = { command, ...params };
            this.ws.send(JSON.stringify(request));
        }

        async getClients() {
            this.send('get_clients');
        }

        async focusClient(windowId) {
            this.send('focus_client', { window: windowId });
        }

        async keyType(text) {
            this.send('key_type', { text });
        }

        async screenshot(mode = 'fullscreen') {
            this.send('screenshot', { mode });
        }
    }

    // Usage
    const client = new DWNClient();
    await client.connect();
    await client.getClients();

### Node.js Client

    const WebSocket = require('ws');

    const ws = new WebSocket('ws://localhost:8777/ws');

    ws.on('open', () => {
        console.log('Connected to DWN');

        // List clients
        ws.send(JSON.stringify({ command: 'get_clients' }));
    });

    ws.on('message', (data) => {
        const response = JSON.parse(data);

        if (response.clients) {
            response.clients.forEach(c => {
                console.log(`${c.title} - ${c.class}`);
            });
        }

        ws.close();
    });

    ws.on('error', (err) => {
        console.error('Error:', err.message);
    });

### Screenshot to Canvas

    async function captureToCanvas(client, canvasId) {
        return new Promise((resolve) => {
            client.ws.onmessage = (event) => {
                const response = JSON.parse(event.data);
                if (response.data) {
                    const img = new Image();
                    img.onload = () => {
                        const canvas = document.getElementById(canvasId);
                        const ctx = canvas.getContext('2d');
                        canvas.width = response.width;
                        canvas.height = response.height;
                        ctx.drawImage(img, 0, 0);
                        resolve();
                    };
                    img.src = 'data:image/png;base64,' + response.data;
                }
            };
            client.send('screenshot', { mode: 'fullscreen' });
        });
    }

## Bash Examples

### Using websocat

    #!/bin/bash

    # List clients
    echo '{"command": "get_clients"}' | websocat ws://localhost:8777/ws

    # Focus client by ID
    echo '{"command": "focus_client", "window": 12345678}' | websocat ws://localhost:8777/ws

    # Switch workspace
    echo '{"command": "switch_workspace", "workspace": 2}' | websocat ws://localhost:8777/ws

    # Take screenshot and save
    echo '{"command": "screenshot", "mode": "fullscreen"}' | \
        websocat ws://localhost:8777/ws | \
        jq -r '.data' | \
        base64 -d > screenshot.png

### Using wscat

    #!/bin/bash

    # One-liner command
    echo '{"command": "get_clients"}' | wscat -c ws://localhost:8777/ws -w 1

    # Interactive session
    wscat -c ws://localhost:8777/ws
    # Then type commands manually

### Using curl with websocat

    #!/bin/bash

    dwn_command() {
        echo "$1" | websocat -n1 ws://localhost:8777/ws
    }

    # Get focused client
    dwn_command '{"command": "get_focused_client"}' | jq '.client.title'

    # Type text
    dwn_command '{"command": "key_type", "text": "Hello!"}'

    # Launch application
    dwn_command '{"command": "run_command", "exec": "firefox"}'

### Fade Effect Control

Control DWN's fade animation effects via API:

    import json
    import websocket

    ws = websocket.create_connection("ws://localhost:8777/ws")

    # Get current fade settings
    ws.send(json.dumps({"command": "get_fade_settings"}))
    response = json.loads(ws.recv())
    print(f"Speed: {response['fade_speed']}, Intensity: {response['fade_intensity']}")

    # Set fade animation speed (0.1 - 3.0)
    ws.send(json.dumps({"command": "set_fade_speed", "speed": 1.5}))

    # Set fade glow intensity (0.0 - 1.0)
    ws.send(json.dumps({"command": "set_fade_intensity", "intensity": 0.8}))

    # Subscribe to fade change events
    ws.send(json.dumps({
        "command": "subscribe",
        "events": ["fade_speed_changed", "fade_intensity_changed"]
    }))

    # Listen for events
    while True:
        event = json.loads(ws.recv())
        if event.get("type") == "event":
            print(f"Event: {event['event']}")
            print(f"Data: {event['data']}")

Using the provided fade control demo script:

    # Get current fade settings
    python3 examples/fade_control_demo.py

    # Set fade speed
    python3 examples/fade_control_demo.py --speed 1.5

    # Set fade intensity
    python3 examples/fade_control_demo.py --intensity 0.8

    # Listen for fade events
    python3 examples/fade_control_demo.py --listen --duration 30

## Event Subscription

### Using dwn_automation_demo.py

The included automation demo script provides ready-to-use event monitoring:

    # List available events
    python3 examples/dwn_automation_demo.py events --list

    # Monitor all events (Ctrl+C to stop)
    python3 examples/dwn_automation_demo.py events --monitor

    # Monitor specific events
    python3 examples/dwn_automation_demo.py events -e window_focused -e workspace_switched

    # Run event demo (10 seconds)
    python3 examples/dwn_automation_demo.py demo events

    # Full event stream demo with summary
    python3 examples/dwn_automation_demo.py demo events-all

### Python Event Listener

    from dwn_api_client import DWNClient

    def on_event(event):
        event_name = event.get("event", "unknown")
        data = event.get("data", {})

        if event_name == "window_focused":
            print(f"Focus: {data.get('title')}")
        elif event_name == "workspace_switched":
            print(f"Workspace: {data.get('new_workspace') + 1}")
        elif event_name == "shortcut_triggered":
            print(f"Shortcut: {data.get('description')}")

        return True  # Continue listening

    client = DWNClient()
    client.connect()

    # Subscribe and listen
    client.listen_events(on_event, events=[
        "window_focused",
        "workspace_switched",
        "shortcut_triggered"
    ])

### Window Activity Logger

    import json
    from datetime import datetime
    from dwn_api_client import DWNClient

    def activity_logger():
        client = DWNClient()
        client.connect()

        client.subscribe(events=[
            "window_focused",
            "window_created",
            "window_destroyed"
        ])

        print("Logging window activity (Ctrl+C to stop)...")

        try:
            with open("activity.log", "a") as f:
                while True:
                    event = client.receive_event(timeout=1.0)
                    if event and event.get("type") == "event":
                        timestamp = datetime.now().isoformat()
                        ev_name = event.get("event")
                        data = event.get("data", {})

                        log_entry = {
                            "time": timestamp,
                            "event": ev_name,
                            "data": data
                        }
                        f.write(json.dumps(log_entry) + "\\n")
                        f.flush()

                        print(f"[{timestamp}] {ev_name}")
        except KeyboardInterrupt:
            print("\\nStopped logging")
        finally:
            client.disconnect()

    activity_logger()

### JavaScript Event Listener

    const ws = new WebSocket('ws://localhost:8777/ws');

    ws.onopen = () => {
        console.log('Connected to DWN');

        // Subscribe to events
        ws.send(JSON.stringify({
            command: 'subscribe',
            events: ['window_focused', 'workspace_switched']
        }));
    };

    ws.onmessage = (event) => {
        const msg = JSON.parse(event.data);

        if (msg.type === 'event') {
            console.log(`Event: ${msg.event}`, msg.data);

            // Handle specific events
            switch (msg.event) {
                case 'window_focused':
                    document.title = msg.data.title || 'DWN';
                    break;
                case 'workspace_switched':
                    updateWorkspaceIndicator(msg.data.new_workspace);
                    break;
            }
        }
    };

    ws.onerror = (err) => {
        console.error('WebSocket error:', err);
    };

    ws.onclose = () => {
        console.log('Disconnected from DWN');
    };

### Reactive Client Arrangement

    from dwn_api_client import DWNClient

    RULES = {
        "code": {"floating": False, "workspace": 0},
        "firefox": {"floating": False, "workspace": 1},
        "slack": {"floating": True, "workspace": 2},
        "telegram": {"floating": True, "workspace": 2},
    }

    def auto_arrange():
        client = DWNClient()
        client.connect()

        client.subscribe(events=["window_created"])

        print("Auto-arranging clients...")

        try:
            while True:
                event = client.receive_event(timeout=1.0)
                if event and event.get("event") == "window_created":
                    data = event.get("data", {})
                    window_id = data.get("window")
                    wm_class = data.get("class", "").lower()

                    for pattern, rules in RULES.items():
                        if pattern in wm_class:
                            print(f"Applying rules to {wm_class}")

                            if "workspace" in rules:
                                client.move_client_to_workspace(
                                    window_id, rules["workspace"]
                                )
                            if "floating" in rules:
                                client.float_client(
                                    window_id, rules["floating"]
                                )
                            break
        except KeyboardInterrupt:
            pass
        finally:
            client.disconnect()

    auto_arrange()

## Automation Recipes

### Auto-Arrange Clients by Class

    from dwn_api_client import DWNClient

    LAYOUT_RULES = {
        "code": {"workspace": 0, "floating": False},
        "firefox": {"workspace": 1, "floating": False},
        "telegram": {"workspace": 2, "floating": True},
        "slack": {"workspace": 2, "floating": True},
    }

    def auto_arrange():
        client = DWNClient()
        client.connect()

        clients = client.get_clients()
        for c in clients:
            wm_class = c["class"].lower()
            for pattern, rules in LAYOUT_RULES.items():
                if pattern in wm_class:
                    if c["workspace"] != rules["workspace"]:
                        client.move_client_to_workspace(
                            c["window"], rules["workspace"]
                        )
                    if c["floating"] != rules["floating"]:
                        client.float_client(c["window"], rules["floating"])
                    break

        client.disconnect()

    auto_arrange()

### Screenshot Monitor

    import time
    import base64
    from datetime import datetime
    from dwn_api_client import DWNClient

    def screenshot_monitor(interval=60, output_dir="screenshots"):
        """Take periodic screenshots"""
        import os
        os.makedirs(output_dir, exist_ok=True)

        client = DWNClient()
        client.connect()

        try:
            while True:
                result = client.screenshot("fullscreen")

                timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
                filename = f"{output_dir}/screen_{timestamp}.png"

                with open(filename, "wb") as f:
                    f.write(base64.b64decode(result["data"]))

                print(f"Saved: {filename}")
                time.sleep(interval)
        except KeyboardInterrupt:
            print("Stopped")
        finally:
            client.disconnect()

    screenshot_monitor(interval=300)  # Every 5 minutes

### Client Focus Logger

    import time
    from datetime import datetime
    from dwn_api_client import DWNClient

    def focus_logger(log_file="focus_log.txt"):
        """Log client focus changes"""
        client = DWNClient()
        client.connect()

        last_focused = None

        try:
            with open(log_file, "a") as f:
                while True:
                    clients = client.get_clients()
                    focused = next((c for c in clients if c["focused"]), None)

                    if focused and focused["window"] != last_focused:
                        timestamp = datetime.now().isoformat()
                        entry = f"{timestamp} | {focused['title']} ({focused['class']})\n"
                        f.write(entry)
                        f.flush()
                        print(entry.strip())
                        last_focused = focused["window"]

                    time.sleep(1)
        except KeyboardInterrupt:
            print("Stopped")
        finally:
            client.disconnect()

    focus_logger()

### Keyboard Macro

    from dwn_api_client import DWNClient
    import time

    def run_macro(client, actions, delay=0.1):
        """Execute a sequence of actions"""
        for action in actions:
            if action["type"] == "key":
                client.key_press(action["key"], action.get("modifiers", []))
            elif action["type"] == "type":
                client.type_text(action["text"])
            elif action["type"] == "click":
                client.mouse_click(action.get("button", 1),
                                 action.get("x"), action.get("y"))
            elif action["type"] == "wait":
                time.sleep(action["seconds"])
            time.sleep(delay)

    # Example: Open terminal and run command
    macro = [
        {"type": "key", "key": "t", "modifiers": ["ctrl", "alt"]},
        {"type": "wait", "seconds": 1},
        {"type": "type", "text": "ls -la"},
        {"type": "key", "key": "Return"},
    ]

    client = DWNClient()
    client.connect()
    run_macro(client, macro)
    client.disconnect()

### OCR Screen Reader

    from dwn_api_client import DWNClient

    def read_active_client():
        """Extract and print text from active client"""
        client = DWNClient()
        client.connect()

        # Capture active client
        screenshot = client.screenshot("active")

        # Extract text
        ocr_result = client.ocr(screenshot["data"])

        print(f"Text from active client (confidence: {ocr_result['confidence']:.0%}):")
        print("-" * 40)
        print(ocr_result["text"])

        client.disconnect()

    read_active_client()

### Browser Automation with OCR

Complete example that opens a browser, performs a Google search, scrolls through results, and extracts text using OCR. See `examples/browser_ocr_demo.py` for the full script.

    #!/usr/bin/env python3
    import asyncio
    import json
    import sys
    import base64
    from typing import List

    class DWNAutomation:
        def __init__(self, host: str = "localhost", port: int = 8777):
            self.uri = f"ws://{host}:{port}/ws"
            self.ws = None

        async def connect(self):
            import websockets
            self.ws = await websockets.connect(self.uri)

        async def disconnect(self):
            if self.ws:
                await self.ws.close()

        async def send_command(self, command: dict) -> dict:
            await self.ws.send(json.dumps(command))
            return json.loads(await self.ws.recv())

        async def run_command(self, exec_cmd: str) -> dict:
            return await self.send_command({"command": "run_command", "exec": exec_cmd})

        async def get_focused_client(self) -> dict:
            return await self.send_command({"command": "get_focused_client"})

        async def focus_client(self, window_id: int) -> dict:
            return await self.send_command({"command": "focus_client", "window": window_id})

        async def key_tap(self, keysym: str, modifiers: List[str] = None) -> dict:
            cmd = {"command": "key_tap", "keysym": keysym}
            if modifiers:
                cmd["modifiers"] = modifiers
            return await self.send_command(cmd)

        async def key_type(self, text: str) -> dict:
            return await self.send_command({"command": "key_type", "text": text})

        async def mouse_scroll(self, direction: str, amount: int = 1) -> dict:
            return await self.send_command({
                "command": "mouse_scroll", "direction": direction, "amount": amount
            })

        async def screenshot(self, mode: str = "active") -> dict:
            return await self.send_command({"command": "screenshot", "mode": mode})

        async def ocr(self, image_base64: str) -> dict:
            return await self.send_command({"command": "ocr", "image": image_base64})

    async def main():
        automation = DWNAutomation()
        await automation.connect()

        # Open default browser with Google
        await automation.run_command("xdg-open https://www.google.nl")
        await asyncio.sleep(5.0)

        # Get focused browser window
        result = await automation.get_focused_client()
        window_id = int(result.get("client", {}).get("window", 0))

        # Search for something
        await automation.key_type("ponies")
        await automation.key_tap("Return")
        await asyncio.sleep(4.0)

        # Scroll and collect OCR text from multiple pages
        all_text = []
        for i in range(4):
            # Take screenshot and run OCR
            screenshot = await automation.screenshot("active")
            image_data = screenshot.get("data", "")

            # Save screenshot
            with open(f"screenshot_{i+1}.png", "wb") as f:
                f.write(base64.b64decode(image_data))

            # Extract text
            ocr_result = await automation.ocr(image_data)
            text = ocr_result.get("text", "").strip()
            if text:
                all_text.append(f"--- Page {i+1} ---\n{text}")

            # Scroll down for next page
            if i < 3:
                await automation.mouse_scroll("down", 5)
                await asyncio.sleep(1.5)

        # Print combined results
        print("EXTRACTED TEXT:")
        print("\n\n".join(all_text))

        await automation.disconnect()

    asyncio.run(main())

Run the included demo script:

    # Install dependency
    pip install websockets

    # Run the demo
    python3 examples/browser_ocr_demo.py