Skip to content

Complete Usage Examples

vue
<template>
  <div class="app-container">
    <div ref="containerRef" class="device-container"></div>
    <div class="controls">
      <div class="input-group">
        <label class="label">Host IP:</label>
        <input v-model="hostIp" placeholder="e.g., 192.168.10.50" class="input" />
        <label class="label">Device Name:</label>
        <input v-model="deviceName" placeholder="e.g., EDGE00M7EHBKZGVN" class="input" />
      </div>
      <div class="button-group">
        <button @click="connect" class="btn btn-primary">Connect</button>
        <button @click="disconnect" class="btn btn-secondary">Disconnect</button>
        <button @click="handleHome" class="btn btn-action">Home</button>
        <button @click="handleBack" class="btn btn-action">Back</button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, shallowRef, markRaw, onMounted, onUnmounted } from "vue";
import {
  VmosEdgeClient,
  VmosEdgeClientEvents,
  type VmosEdgeClientConfig,
  type VmosEdgeErrorEvent,
} from "@vmosedge/web-sdk";

const containerRef = ref<HTMLElement | null>(null);
const client = shallowRef<VmosEdgeClient | null>(null);
const hostIp = ref("192.168.10.50"); // Host IP
const deviceName = ref("EDGE00M7EHBKZGVN"); // Device name

// API response type definitions
interface DeviceInfo {
  ip: string;
  host_ip: string;
  db_id: string;
  network_mode: string;
  is_macvlan: boolean;
  tcp_port: number;
  tcp_control_port: number;
}

interface ApiResponse {
  code: number;
  msg: string;
  data: {
    count: number;
    host_ip: string;
    list: DeviceInfo[];
  };
}

// Get device information and build configuration
const getDeviceConfig = async (
  hostIp: string,
  deviceName: string
): Promise<VmosEdgeClientConfig> => {
  const response = await fetch(`http://${hostIp}:18182/container_api/v1/get_db`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ name: deviceName }),
  });

  const result: ApiResponse = await response.json();

  if (result.code !== 200 || !result.data.list || result.data.list.length === 0) {
    throw new Error(result.msg || "Failed to get device information");
  }

  const device = result.data.list[0];
  const isMacvlan = device.network_mode === "macvlan" || device.is_macvlan;

  // Build configuration based on network mode
  if (isMacvlan) {
    // LAN mode (macvlan)
    return {
      ip: device.ip, // Use device's ip field
      deviceId: device.db_id, // Use device's db_id field
      ports: {
        video: 9999, // LAN mode fixed port
        touch: 9997, // LAN mode fixed port
      },
    };
  } else {
    // Non-LAN mode (bridge)
    return {
      ip: result.data.host_ip, // Use host_ip field from data level
      deviceId: device.db_id, // Use device's db_id field
      ports: {
        video: device.tcp_port, // Use device's tcp_port field
        touch: device.tcp_control_port, // Use device's tcp_control_port field
      },
    };
  }
};

const connect = async () => {
  if (!containerRef.value) return;

  // Environment detection
  if (!VmosEdgeClient.isWebCodecsSupported()) {
    console.error("Current environment does not support SDK usage. Please open the HTML file using file:// protocol");
    return;
  }

  try {
    // Get device configuration
    const config = await getDeviceConfig(hostIp.value, deviceName.value);

    const instance = new VmosEdgeClient({
      container: containerRef.value,
      config,
      retryCount: 3,
    });

    // Use markRaw to avoid Vue reactive proxy
    client.value = markRaw(instance);

    // Listen to events
    client.value.on(VmosEdgeClientEvents.STARTED, () => {
      console.log("Connected successfully");
    });

    client.value.on(VmosEdgeClientEvents.ERROR, (error: VmosEdgeErrorEvent) => {
      console.error("Error:", error);
    });

    await client.value.start();
  } catch (error) {
    console.error("Connection failed:", error);
  }
};

const disconnect = () => {
  client.value?.stop();
  client.value = null;
};

const handleHome = () => {
  client.value?.home();
};

const handleBack = () => {
  client.value?.back();
};

onUnmounted(() => {
  disconnect();
});
</script>

<style scoped>
.app-container {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 60px;
  padding: 40px;
  height: 100vh;
  overflow: hidden;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  background: #fff;
}

.device-container {
  width: 360px;
  height: 720px;
  background: #fff;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  flex-shrink: 0;
  overflow: hidden;
}

.controls {
  display: flex;
  flex-direction: column;
  gap: 20px;
  width: 320px;
  flex-shrink: 0;
  max-height: 100vh;
  overflow: visible;
}

.input-group {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.label {
  font-size: 14px;
  font-weight: 500;
  color: #333;
  margin-bottom: 0;
}

.input {
  padding: 12px 16px;
  border: 1px solid #e0e0e0;
  border-radius: 6px;
  font-size: 14px;
  transition: all 0.2s;
  outline: none;
  width: 100%;
  background: #fff;
}

.input:focus {
  border-color: #4a90e2;
  box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
}

.button-group {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.btn {
  padding: 12px 20px;
  border: none;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s;
  outline: none;
  min-width: 0;
}

.btn-primary {
  background: #4a90e2;
  color: white;
  grid-column: 1 / -1;
}

.btn-primary:hover {
  background: #357abd;
}

.btn-secondary {
  background: #6c757d;
  color: white;
}

.btn-secondary:hover {
  background: #5a6268;
}

.btn-action {
  background: #f8f9fa;
  color: #333;
  border: 1px solid #dee2e6;
}

.btn-action:hover {
  background: #e9ecef;
}
</style>
tsx
import { useEffect, useRef, useState } from "react";
import {
  VmosEdgeClient,
  VmosEdgeClientEvents,
  type VmosEdgeClientConfig,
  type VmosEdgeErrorEvent,
} from "@vmosedge/web-sdk";

// API response type definitions
interface DeviceInfo {
  ip: string;
  host_ip: string;
  db_id: string;
  network_mode: string;
  is_macvlan: boolean;
  tcp_port: number;
  tcp_control_port: number;
}

interface ApiResponse {
  code: number;
  msg: string;
  data: {
    count: number;
    host_ip: string;
    list: DeviceInfo[];
  };
}

function App() {
  const containerRef = useRef<HTMLDivElement>(null);
  const clientRef = useRef<VmosEdgeClient | null>(null);
  const [connected, setConnected] = useState(false);
  const [hostIp, setHostIp] = useState("192.168.10.50"); // Host IP
  const [deviceName, setDeviceName] = useState("EDGE00M7EHBKZGVN"); // Device name

  // Get device information and build configuration
  const getDeviceConfig = async (
    hostIp: string,
    deviceName: string
  ): Promise<VmosEdgeClientConfig> => {
    const response = await fetch(`http://${hostIp}:18182/container_api/v1/get_db`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ name: deviceName }),
    });

    const result: ApiResponse = await response.json();

    if (result.code !== 200 || !result.data.list || result.data.list.length === 0) {
      throw new Error(result.msg || "Failed to get device information");
    }

    const device = result.data.list[0];
    const isMacvlan = device.network_mode === "macvlan" || device.is_macvlan;

    // Build configuration based on network mode
    if (isMacvlan) {
      // LAN mode (macvlan)
      return {
        ip: device.ip, // Use device's ip field
        deviceId: device.db_id, // Use device's db_id field
        ports: {
          video: 9999, // LAN mode fixed port
          touch: 9997, // LAN mode fixed port
        },
      };
    } else {
      // Non-LAN mode (bridge)
      return {
        ip: result.data.host_ip, // Use host_ip field from data level
        deviceId: device.db_id, // Use device's db_id field
        ports: {
          video: device.tcp_port, // Use device's tcp_port field
          touch: device.tcp_control_port, // Use device's tcp_control_port field
        },
      };
    }
  };

  const connect = async () => {
    if (!containerRef.current) return;

    // Environment detection
    if (!VmosEdgeClient.isWebCodecsSupported()) {
      console.error("Current environment does not support SDK usage. Please open the HTML file using file:// protocol, or access through localhost");
      return;
    }

    try {
      // Get device configuration
      const config = await getDeviceConfig(hostIp, deviceName);

      const client = new VmosEdgeClient({
        container: containerRef.current,
        config,
        retryCount: 3,
      });

      clientRef.current = client;

      client.on(VmosEdgeClientEvents.STARTED, () => {
        setConnected(true);
        console.log("Connected successfully");
      });

      client.on(VmosEdgeClientEvents.ERROR, (error: VmosEdgeErrorEvent) => {
        console.error("Error:", error);
      });

      await client.start();
    } catch (error) {
      console.error("Connection failed:", error);
    }
  };

  const disconnect = () => {
    clientRef.current?.stop();
    clientRef.current = null;
    setConnected(false);
  };

  useEffect(() => {
    return () => {
      disconnect();
    };
  }, []);

  return (
    <div style={styles.container}>
      <div ref={containerRef} style={styles.deviceContainer} />
      <div style={styles.controls}>
        <div style={styles.inputGroup}>
          <label style={styles.label}>Host IP:</label>
          <input
            value={hostIp}
            onChange={(e) => setHostIp(e.target.value)}
            placeholder="e.g., 192.168.10.50"
            style={styles.input}
          />
          <label style={styles.label}>Device Name:</label>
          <input
            value={deviceName}
            onChange={(e) => setDeviceName(e.target.value)}
            placeholder="e.g., EDGE00M7EHBKZGVN"
            style={styles.input}
          />
        </div>
        <div style={styles.buttonGroup}>
          <button onClick={connect} style={{ ...styles.btn, ...styles.btnPrimary }}>
            Connect
          </button>
          <button onClick={disconnect} style={{ ...styles.btn, ...styles.btnSecondary }}>
            Disconnect
          </button>
          <button onClick={() => clientRef.current?.home()} style={{ ...styles.btn, ...styles.btnAction }}>
            Home
          </button>
          <button onClick={() => clientRef.current?.back()} style={{ ...styles.btn, ...styles.btnAction }}>
            Back
          </button>
        </div>
      </div>
    </div>
  );
}

const styles = {
  container: {
    display: "flex",
    flexDirection: "row" as const,
    alignItems: "center",
    justifyContent: "center",
    gap: "40px",
    padding: "20px",
    minHeight: "100vh",
    maxHeight: "100vh",
    overflow: "hidden",
    fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
  },
  deviceContainer: {
    width: 360,
    height: 720,
    background: "#fff",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    borderRadius: "12px",
    boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)",
    flexShrink: 0,
    overflow: "hidden",
  },
  controls: {
    display: "flex",
    flexDirection: "column" as const,
    gap: "16px",
    width: "360px",
    flexShrink: 0,
  },
  inputGroup: {
    display: "flex",
    flexDirection: "column" as const,
    gap: "8px",
  },
  label: {
    fontSize: "14px",
    fontWeight: 500,
    color: "#333",
    marginBottom: "4px",
  },
  input: {
    padding: "12px 16px",
    border: "1px solid #e0e0e0",
    borderRadius: "6px",
    fontSize: "14px",
    transition: "all 0.2s",
    outline: "none",
  },
  buttonGroup: {
    display: "flex",
    gap: "8px",
    flexWrap: "wrap" as const,
  },
  btn: {
    padding: "10px 20px",
    border: "none",
    borderRadius: "6px",
    fontSize: "14px",
    fontWeight: 500,
    cursor: "pointer",
    transition: "all 0.2s",
    outline: "none",
  },
  btnPrimary: {
    background: "#4a90e2",
    color: "white",
    flex: 1,
  },
  btnSecondary: {
    background: "#6c757d",
    color: "white",
    flex: 1,
  },
  btnAction: {
    background: "#f8f9fa",
    color: "#333",
    border: "1px solid #dee2e6",
  },
};

export default App;
html
<!DOCTYPE html>
<html>
<head>
  <title>Vmos Edge Web SDK Example</title>
  <style>
    * {
      box-sizing: border-box;
    }
    html, body {
      margin: 0;
      padding: 0;
      height: 100%;
      overflow: hidden;
    }
    body {
      padding: 20px;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      display: flex;
      flex-direction: row;
      align-items: center;
      justify-content: center;
      gap: 40px;
      min-height: 100vh;
      max-height: 100vh;
      background: #f5f5f5;
    }
    #container {
      width: 360px;
      height: 720px;
      background: #fff;
      display: flex;
      justify-content: center;
      align-items: center;
      border-radius: 12px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      flex-shrink: 0;
      overflow: hidden;
    }
    .controls {
      display: flex;
      flex-direction: column;
      gap: 16px;
      width: 360px;
      flex-shrink: 0;
    }
    .input-group {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    .input-group label {
      font-size: 14px;
      font-weight: 500;
      color: #333;
      margin-bottom: 4px;
    }
    .input-group input {
      padding: 12px 16px;
      border: 1px solid #e0e0e0;
      border-radius: 6px;
      font-size: 14px;
      transition: all 0.2s;
      outline: none;
    }
    .input-group input:focus {
      border-color: #4a90e2;
      box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
    }
    .button-group {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
    }
    .button-group button {
      padding: 10px 20px;
      border: none;
      border-radius: 6px;
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.2s;
      outline: none;
    }
    .button-group button:hover {
      transform: translateY(-1px);
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }
    .btn-primary {
      background: #4a90e2;
      color: white;
      flex: 1;
    }
    .btn-primary:hover {
      background: #357abd;
    }
    .btn-secondary {
      background: #6c757d;
      color: white;
      flex: 1;
    }
    .btn-secondary:hover {
      background: #5a6268;
    }
    .btn-action {
      background: #f8f9fa;
      color: #333;
      border: 1px solid #dee2e6;
    }
    .btn-action:hover {
      background: #e9ecef;
    }
  </style>
</head>
<body>
  <div id="container"></div>
  <div class="controls">
    <div class="input-group">
      <label>Host IP:</label>
      <input id="hostIp" type="text" placeholder="e.g., 192.168.10.50" value="192.168.10.50" />
      <label>Device Name:</label>
      <input id="deviceName" type="text" placeholder="e.g., EDGE00M7EHBKZGVN" value="EDGE00M7EHBKZGVN" />
    </div>
    <div class="button-group">
      <button onclick="connect()" class="btn-primary">Connect</button>
      <button onclick="disconnect()" class="btn-secondary">Disconnect</button>
      <button onclick="handleHome()" class="btn-action">Home</button>
      <button onclick="handleBack()" class="btn-action">Back</button>
    </div>
  </div>

  <script type="module">
    import {
      VmosEdgeClient,
      VmosEdgeClientEvents,
    } from "@vmosedge/web-sdk";

    let client = null;

    // Get device information and build configuration
    async function getDeviceConfig(hostIp, deviceName) {
      const response = await fetch(`http://${hostIp}:18182/container_api/v1/get_db`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ name: deviceName }),
      });

      const result = await response.json();

      if (result.code !== 200 || !result.data.list || result.data.list.length === 0) {
        throw new Error(result.msg || "Failed to get device information");
      }

      const device = result.data.list[0];
      const isMacvlan = device.network_mode === "macvlan" || device.is_macvlan;

      // Build configuration based on network mode
      if (isMacvlan) {
        // LAN mode (macvlan)
        return {
          ip: device.ip, // Use device's ip field
          deviceId: device.db_id, // Use device's db_id field
          ports: {
            video: 9999, // LAN mode fixed port
            touch: 9997, // LAN mode fixed port
          },
        };
      } else {
        // Non-LAN mode (bridge)
        return {
          ip: result.data.host_ip, // Use host_ip field from data level
          deviceId: device.db_id, // Use device's db_id field
          ports: {
            video: device.tcp_port, // Use device's tcp_port field
            touch: device.tcp_control_port, // Use device's tcp_control_port field
          },
        };
      }
    }

    async function connect() {
      const container = document.getElementById("container");
      const hostIp = document.getElementById("hostIp").value;
      const deviceName = document.getElementById("deviceName").value;

      // Environment detection
      if (!VmosEdgeClient.isWebCodecsSupported()) {
        console.error("Current environment does not support SDK usage. Please open the HTML file using file:// protocol, or access through localhost");
        return;
      }

      try {
        // Get device configuration
        const config = await getDeviceConfig(hostIp, deviceName);

        client = new VmosEdgeClient({
          container,
          config,
          retryCount: 3,
        });

        client.on(VmosEdgeClientEvents.STARTED, () => {
          console.log("Connected successfully");
        });

        client.on(VmosEdgeClientEvents.ERROR, (error) => {
          console.error("Error:", error);
        });

        await client.start();
      } catch (error) {
        console.error("Connection failed:", error);
      }
    }

    function disconnect() {
      if (client) {
        client.stop();
        client = null;
      }
    }

    function handleHome() {
      client?.home();
    }

    function handleBack() {
      client?.back();
    }

    // Export to global scope
    window.connect = connect;
    window.disconnect = disconnect;
    window.handleHome = handleHome;
    window.handleBack = handleBack;
  </script>
</body>
</html>

⚠️ Important Notes

Vue/React Framework Usage

Important: When using in Vue or React, VmosEdgeClient instances should NOT be deeply proxied.

  • Vue 3: Use shallowRef and markRaw
  • React: Use useRef

Wrong Example (Vue):

typescript
// ❌ Wrong: Using ref will cause Proxy proxy issues
const client = ref<VmosEdgeClient | null>(null);
client.value = new VmosEdgeClient({ /* ... */ });

Correct Example (Vue):

typescript
// ✅ Correct: Use shallowRef and markRaw
const client = shallowRef<VmosEdgeClient | null>(null);
const instance = new VmosEdgeClient({ /* ... */ });
client.value = markRaw(instance);

Container Element

  • Container element must be a valid HTMLElement
  • SDK will create a .vmos-canvas-container element inside the container
  • It's recommended to set fixed dimensions for the container to avoid layout issues

Connection Configuration

  • Single Control Mode: Both video and touch ports are required
  • Group Control Mode: Slave devices only need touch port, video port is not required
  • audio port is not supported, do not configure it
  • Ensure device IP and port configuration are correct

Error Handling

  • Always listen to ERROR events and handle connection errors promptly
  • Handle accordingly based on error type (error.type) and error code (error.code)

Resource Cleanup

  • Always call client.stop() to release resources when components unmount or pages close
  • Avoid memory leaks

Powered by VMOS Edge Team