Files
geutebruck/geutebruck-api/docs/usage-guide.md
Administrator 14893e62a5 feat: Geutebruck GeViScope/GeViSoft Action Mapping System - MVP
This MVP release provides a complete full-stack solution for managing action mappings
in Geutebruck's GeViScope and GeViSoft video surveillance systems.

## Features

### Flutter Web Application (Port 8081)
- Modern, responsive UI for managing action mappings
- Action picker dialog with full parameter configuration
- Support for both GSC (GeViScope) and G-Core server actions
- Consistent UI for input and output actions with edit/delete capabilities
- Real-time action mapping creation, editing, and deletion
- Server categorization (GSC: prefix for GeViScope, G-Core: prefix for G-Core servers)

### FastAPI REST Backend (Port 8000)
- RESTful API for action mapping CRUD operations
- Action template service with comprehensive action catalog (247 actions)
- Server management (G-Core and GeViScope servers)
- Configuration tree reading and writing
- JWT authentication with role-based access control
- PostgreSQL database integration

### C# SDK Bridge (gRPC, Port 50051)
- Native integration with GeViSoft SDK (GeViProcAPINET_4_0.dll)
- Action mapping creation with correct binary format
- Support for GSC and G-Core action types
- Proper Camera parameter inclusion in action strings (fixes CrossSwitch bug)
- Action ID lookup table with server-specific action IDs
- Configuration reading/writing via SetupClient

## Bug Fixes
- **CrossSwitch Bug**: GSC and G-Core actions now correctly display camera/PTZ head parameters in GeViSet
- Action strings now include Camera parameter: `@ PanLeft (Comment: "", Camera: 101028)`
- Proper filter flags and VideoInput=0 for action mappings
- Correct action ID assignment (4198 for GSC, 9294 for G-Core PanLeft)

## Technical Stack
- **Frontend**: Flutter Web, Dart, Dio HTTP client
- **Backend**: Python FastAPI, PostgreSQL, Redis
- **SDK Bridge**: C# .NET 8.0, gRPC, GeViSoft SDK
- **Authentication**: JWT tokens
- **Configuration**: GeViSoft .set files (binary format)

## Credentials
- GeViSoft/GeViScope: username=sysadmin, password=masterkey
- Default admin: username=admin, password=admin123

## Deployment
All services run on localhost:
- Flutter Web: http://localhost:8081
- FastAPI: http://localhost:8000
- SDK Bridge gRPC: localhost:50051
- GeViServer: localhost (default port)

Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-31 18:10:54 +01:00

11 KiB

API Usage Guide

Practical examples for using the Geutebruck Cross-Switching API.


Getting Started

1. Login

First, authenticate to get your access token:

Request:

curl -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "admin",
    "password": "admin123"
  }'

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600
}

Save the access token for subsequent requests.


Common Operations

Discover Available Cameras

curl -X GET http://localhost:8000/api/v1/cameras \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "cameras": [
    {
      "id": 1,
      "name": "Entrance Camera",
      "status": "online",
      "has_ptz": true
    },
    {
      "id": 2,
      "name": "Parking Lot Camera",
      "status": "online",
      "has_ptz": false
    }
  ],
  "total": 2
}

Discover Available Monitors

curl -X GET http://localhost:8000/api/v1/monitors \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "monitors": [
    {
      "id": 1,
      "name": "Control Room Monitor 1",
      "status": "idle",
      "current_camera_id": null
    },
    {
      "id": 2,
      "name": "Control Room Monitor 2",
      "status": "active",
      "current_camera_id": 5
    }
  ],
  "total": 2
}

Find Available (Idle) Monitors

curl -X GET http://localhost:8000/api/v1/monitors/filter/available \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Returns only monitors with no camera currently assigned.


Cross-Switching Operations

Route Camera to Monitor

⚠️ Requires Operator role or higher

curl -X POST http://localhost:8000/api/v1/crossswitch \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "camera_id": 1,
    "monitor_id": 1,
    "mode": 0
  }'

Response:

{
  "success": true,
  "message": "Successfully switched camera 1 to monitor 1",
  "route": {
    "id": "uuid",
    "camera_id": 1,
    "monitor_id": 1,
    "executed_at": "2025-12-09T12:00:00Z",
    "executed_by_username": "operator"
  }
}

Clear Monitor

⚠️ Requires Operator role or higher

curl -X POST http://localhost:8000/api/v1/crossswitch/clear \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "monitor_id": 1
  }'

Get Current Routing State

curl -X GET http://localhost:8000/api/v1/crossswitch/routing \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "routes": [
    {
      "camera_id": 1,
      "monitor_id": 1,
      "executed_at": "2025-12-09T12:00:00Z",
      "is_active": true
    }
  ],
  "total": 1
}

Get Routing History

curl -X GET "http://localhost:8000/api/v1/crossswitch/history?limit=10&offset=0" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Filter by camera:

curl -X GET "http://localhost:8000/api/v1/crossswitch/history?camera_id=1" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Filter by monitor:

curl -X GET "http://localhost:8000/api/v1/crossswitch/history?monitor_id=1" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Use Case Examples

Use Case 1: Quick Camera Check

Scenario: Operator wants to quickly view entrance camera on their monitor.

Steps:

  1. Find available monitor
  2. Route entrance camera to that monitor
# Step 1: Find available monitors
curl -X GET http://localhost:8000/api/v1/monitors/filter/available \
  -H "Authorization: Bearer $TOKEN"

# Step 2: Route camera 1 to monitor 1
curl -X POST http://localhost:8000/api/v1/crossswitch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"camera_id": 1, "monitor_id": 1}'

Use Case 2: Monitor Rotation

Scenario: Automatically rotate through cameras on a monitor.

Script (PowerShell):

$token = "YOUR_ACCESS_TOKEN"
$monitor_id = 1
$cameras = @(1, 2, 3, 4)  # Camera IDs to rotate

foreach ($camera_id in $cameras) {
    # Switch camera
    Invoke-RestMethod -Uri "http://localhost:8000/api/v1/crossswitch" `
        -Method POST `
        -Headers @{ "Authorization" = "Bearer $token" } `
        -ContentType "application/json" `
        -Body (@{ camera_id = $camera_id; monitor_id = $monitor_id } | ConvertTo-Json)

    # Wait 10 seconds
    Start-Sleep -Seconds 10
}

Use Case 3: Incident Response

Scenario: Security incident detected, switch multiple cameras to control room monitors.

# Cameras 1-4 to monitors 1-4
for i in {1..4}; do
  curl -X POST http://localhost:8000/api/v1/crossswitch \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"camera_id\": $i, \"monitor_id\": $i}"
done

Use Case 4: Audit Trail Review

Scenario: Review who accessed which cameras today.

# Get routing history for today
curl -X GET "http://localhost:8000/api/v1/crossswitch/history?limit=100" \
  -H "Authorization: Bearer $TOKEN" \
  | jq '.history[] | select(.executed_at | startswith("2025-12-09"))'

Python Client Example

import requests

class GeutebruckAPI:
    def __init__(self, base_url="http://localhost:8000", username="admin", password="admin123"):
        self.base_url = base_url
        self.token = None
        self.login(username, password)

    def login(self, username, password):
        """Authenticate and get token"""
        response = requests.post(
            f"{self.base_url}/api/v1/auth/login",
            json={"username": username, "password": password}
        )
        response.raise_for_status()
        self.token = response.json()["access_token"]

    def _headers(self):
        return {"Authorization": f"Bearer {self.token}"}

    def list_cameras(self):
        """Get all cameras"""
        response = requests.get(
            f"{self.base_url}/api/v1/cameras",
            headers=self._headers()
        )
        return response.json()

    def list_monitors(self):
        """Get all monitors"""
        response = requests.get(
            f"{self.base_url}/api/v1/monitors",
            headers=self._headers()
        )
        return response.json()

    def crossswitch(self, camera_id, monitor_id, mode=0):
        """Execute cross-switch"""
        response = requests.post(
            f"{self.base_url}/api/v1/crossswitch",
            headers=self._headers(),
            json={
                "camera_id": camera_id,
                "monitor_id": monitor_id,
                "mode": mode
            }
        )
        return response.json()

    def clear_monitor(self, monitor_id):
        """Clear monitor"""
        response = requests.post(
            f"{self.base_url}/api/v1/crossswitch/clear",
            headers=self._headers(),
            json={"monitor_id": monitor_id}
        )
        return response.json()

    def get_routing_state(self):
        """Get current routing state"""
        response = requests.get(
            f"{self.base_url}/api/v1/crossswitch/routing",
            headers=self._headers()
        )
        return response.json()


# Usage Example
api = GeutebruckAPI()

# List cameras
cameras = api.list_cameras()
print(f"Found {cameras['total']} cameras")

# Route camera 1 to monitor 1
result = api.crossswitch(camera_id=1, monitor_id=1)
print(f"Cross-switch: {result['message']}")

# Get routing state
routing = api.get_routing_state()
print(f"Active routes: {routing['total']}")

C# Client Example

using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

public class GeutebruckApiClient
{
    private readonly HttpClient _client;
    private string _accessToken;

    public GeutebruckApiClient(string baseUrl = "http://localhost:8000")
    {
        _client = new HttpClient { BaseAddress = new Uri(baseUrl) };
    }

    public async Task LoginAsync(string username, string password)
    {
        var response = await _client.PostAsJsonAsync("/api/v1/auth/login", new
        {
            username,
            password
        });

        response.EnsureSuccessStatusCode();
        var result = await response.Content.ReadFromJsonAsync<LoginResponse>();
        _accessToken = result.AccessToken;
        _client.DefaultRequestHeaders.Authorization =
            new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _accessToken);
    }

    public async Task<CameraListResponse> ListCamerasAsync()
    {
        var response = await _client.GetAsync("/api/v1/cameras");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<CameraListResponse>();
    }

    public async Task<CrossSwitchResponse> ExecuteCrossSwitchAsync(int cameraId, int monitorId, int mode = 0)
    {
        var response = await _client.PostAsJsonAsync("/api/v1/crossswitch", new
        {
            camera_id = cameraId,
            monitor_id = monitorId,
            mode
        });

        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<CrossSwitchResponse>();
    }
}

// Usage
var api = new GeutebruckApiClient();
await api.LoginAsync("admin", "admin123");

var cameras = await api.ListCamerasAsync();
Console.WriteLine($"Found {cameras.Total} cameras");

var result = await api.ExecuteCrossSwitchAsync(cameraId: 1, monitorId: 1);
Console.WriteLine($"Cross-switch: {result.Message}");

Testing with Postman

  1. Import Collection: Import the OpenAPI spec from http://localhost:8000/openapi.json
  2. Set Environment Variable: Create access_token variable
  3. Login: Run POST /api/v1/auth/login, save token to environment
  4. Test Endpoints: All subsequent requests will use the token automatically

Troubleshooting

401 Unauthorized

Problem: Token expired or invalid.

Solution: Re-authenticate:

# Get new token
curl -X POST http://localhost:8000/api/v1/auth/login \
  -d '{"username":"admin","password":"admin123"}'

403 Forbidden

Problem: User role insufficient (e.g., Viewer trying to execute cross-switch).

Solution: Use account with Operator or Administrator role.

404 Not Found

Problem: Camera or monitor ID doesn't exist.

Solution: List cameras/monitors to find valid IDs.

500 Internal Server Error

Problem: SDK Bridge communication failure or database error.

Solution:

  1. Check health endpoint: /health
  2. Verify SDK Bridge is running
  3. Check API logs

Best Practices

  1. Always check health before operations
  2. Cache camera/monitor lists (refreshed every 60s)
  3. Handle 401 errors by re-authenticating
  4. Use refresh tokens to extend sessions
  5. Log all cross-switch operations to external system
  6. Implement retry logic for transient failures
  7. Monitor audit logs for security events

Next Steps

  • Explore interactive documentation: http://localhost:8000/docs
  • Review API reference: docs/api-reference.md
  • Check deployment guide: docs/deployment.md
  • Review architecture: docs/architecture.md