@zjzg/runde

润得安全帽 js 调用 SDK

Usage no npm install needed!

<script type="module">
  import zjzgRunde from 'https://cdn.skypack.dev/@zjzg/runde';
</script>

README

物联网平台 SIP 安全帽视频通话库

润得安全帽 js 调用 SDK

安装

   npm i @zjzg/runde

使用

/**
 * 引入依赖
 */
import { Runde } from "../runde";
import {
  CALL_STATE,
  CMD,
  CMDMessage,
  DeviceInfo,
  SosInfo,
  UserInfo,
} from "../runde/class";

/**
 * 开始使用
 */
rende = new Runde({
  name: "xxxxx",
  password: "xxxxx",
  host: "https://xxxxxx.runde.xxx",
  wsurl: "ws://xxxx.runde.xxx:10000",
  poolDuration: 20000, //轮询心跳的间隔默认5秒 范围应该小于30秒
});
rende.addListener({
  cmdMessage: (e: CMDMessage) => {
    console.log("接收的回调", e);
    if (e.cmd == CMD.GET_ONLINE_DEVICES) {
      li.value = e.data;
    } else if (e.cmd == CMD.SOS) {
      sosInfo.value = e;
      showSos.value = true;
      setTimeout(() => {
        //30秒未响应就挂断了
        showSos.value = false;
      }, 30000);
    }
  },
  callState: (type: CALL_STATE, response?: any) => {
    console.log("通话状态:===", type, response);
    callState.value = type;
  },
});

/**
 * 在页面销毁时销毁实例
 */
rende?.destroy();
rende = null;

完整 Vue3 的使用示例

<script setup lang="ts">
import { IncomingResponseMessage } from "sip.js/lib/core";
import { onBeforeUnmount } from "vue";
import { onMounted, ref } from "vue";
import { Runde } from "../runde";
import {
  CALL_STATE,
  CMD,
  CMDMessage,
  DeviceInfo,
  SosInfo,
  UserInfo,
} from "../runde/class";
defineProps<{ msg: string }>();

const count = ref(0);
var rende: Runde | null;
const li = ref<DeviceInfo[]>([]);
const sosInfo = ref<SosInfo>();
const showSos = ref(false);
const video = ref<HTMLMediaElement>();
const callState = ref("");
onMounted(() => {
  rende = new Runde({
    name: "xxxxx",
    password: "xxxxx",
    host: "https://xxxxxx.runde.xxx",
    wsurl: "ws://xxxx.runde.xxx:10000",
    poolDuration: 20000,
  });
  rende.addListener({
    cmdMessage: (e: CMDMessage) => {
      console.log("接收的回调", e);
      if (e.cmd == CMD.GET_ONLINE_DEVICES) {
        li.value = e.data;
      } else if (e.cmd == CMD.SOS) {
        sosInfo.value = e;
        showSos.value = true;
        setTimeout(() => {
          //30秒未响应就挂断了
          showSos.value = false;
        }, 30000);
      }
    },
    callState: (type: CALL_STATE, response?: any) => {
      console.log("通话状态:===", type, response);
      callState.value = type;
    },
  });
});
onBeforeUnmount(() => {
  rende?.destroy();
  rende = null;
});
const getDevice = function () {
  rende?.getDevice();
};
const call = function (user?: UserInfo, type?: string) {
  if (user && video.value) {
    rende?.call(user, video.value, type || "1");
  }
};
const sendMessage = function (device: DeviceInfo) {
  if (device.user_info?.user_id) {
    rende?.sendSMS(device.user_info?.user_id, smsInput.value.value);
  }
};
const hangUp = () => {
  rende?.hangUp();
};
const enterRoom = () => {
  if (video.value) {
    rende?.enterRoom(sosInfo.value, video.value, "2");
  }
};
const smsInput = ref();
</script>

<template>
  <div v-if="showSos">
    <div>
      {{
        `${sosInfo?.ca_info?.department} - 的 -> ${sosInfo?.ca_info?.user_name} `
      }}来电
    </div>
    <button @click="enterRoom()">接听</button>
  </div>
  <input type="text" ref="smsInput" />
  <p @click="getDevice">获取设备信息</p>
  <template v-for="(item, i) in li" :key="i">
    <div style="display: flex">
      <span style="padding: 8px"> {{ item.user_info?.user_name }}</span>
      <span style="padding: 8px"> {{ item.user_info?.sip_id }}</span>
      <div style="padding: 8px" @click="call(item.user_info, '1')">语音</div>
      <div style="padding: 8px" @click="call(item.user_info, '2')">视频</div>
      <div style="padding: 8px" @click="sendMessage(item)">发消息</div>
    </div>
  </template>
  <div>通话状态:{{ callState }}</div>
  <video
    ref="video"
    style="height: 250px; width: 400px"
    id="remoteVideo"
  ></video>
  <button type="button" @click="hangUp()">挂断</button>
</template>

<style scoped>
a {
  color: #42b983;
}

label {
  margin: 0 0.5em;
  font-weight: bold;
}

code {
  background-color: #eee;
  padding: 2px 4px;
  border-radius: 4px;
  color: #304455;
}
</style>
import ReconnectingWebSocket from "reconnecting-websocket";
import { SimpleUser } from "sip.js/lib/platform/web";
import { IncomingResponseMessage } from "sip.js/lib/core";
import {
  AdminInfo,
  CALL_STATE,
  CMDMessage,
  MsgListener,
  RundeOpt,
  SosInfo,
  UserInfo,
} from "./class";
/**
 * 润德api实体类
 */
export declare class Runde {
  udid: string;
  connectState: number;
  opt: RundeOpt | undefined;
  rws: ReconnectingWebSocket | undefined;
  admin_info: AdminInfo | undefined | null;
  onlineDevices: Array<any>;
  listeners: Array<MsgListener>;
  heartPool: number;
  sUser: SimpleUser | undefined;
  toUserSip: string;
  el: HTMLMediaElement | null;
  callType: string;
  constructor(opt: RundeOpt);
  /**
   * 初始化
   * @param opt
   */
  init(opt: RundeOpt): void;
  /**
   * 初始化 SIP
   */
  _initSip(): void;
  /**
   *  添加监听
   * @param listener
   * @returns 监听的key 方便取消监听用
   */
  addListener(listener: MsgListener): string;
  /**
   * 根据key取消监听
   */
  removeListener(key: string): void;
  /**
   * 发送 通话状态
   * @param type
   * @param data
   */
  emitCallState(type: CALL_STATE, data?: any): void;
  /**
   * 登录
   * 如果传入账号密码会覆盖通过设置配置的账号密码
   */
  login(name?: string, password?: string): void;
  /**
   * 发送获取当前在线设备的指令信息
   */
  getDevice(): void;
  /**
   * 发送指令或信息
   * @param msg
   */
  sendMessage(msg: any): void;
  /**
   * 开启通话
   * @param user_id
   * @param to_sip
   * @param el 播放视频的DOM元素
   * @param type 1音频 2视频
   */
  call(user: UserInfo, el: HTMLMediaElement, type: string): void;
  /**
   * 响应SOS 进入房间
   * @param sos
   * @param el 播放视频的DOM元素
   * @param type 1音频 2视频
   */
  enterRoom(sos: SosInfo | undefined, el: HTMLMediaElement, type: string): void;
  /**
   * SIP 拨打电话
   * @param toUser
   */
  _sipCall(toUser: string): void;
  _emitRequestDelegate(
    type: CALL_STATE,
    response: IncomingResponseMessage
  ): void;
  /**
   * 挂断通话
   */
  hangUp(): void;
  /**
   * 发送消息
   * @param user_id 用户ID
   * @param msg 消息
   * @returns
   */
  sendSMS(user_id: string, msg: string): void;
  /**
   * 解析ws返回的指令信息,遍历并广播给监听器
   * @param data
   */
  onMessage(data: CMDMessage): void;
  /**
   * 设置video
   * @param el
   */
  setRemoteMedia(): void;
  /**
   * 释放 media 资源
   */
  cleanupMedia(): void;
  /**
   * 心跳,不然后台会主动断开ws连接
   * 登录成功后自动开启3秒轮询心跳
   */
  starHeartbeat(): void;
  /**
   * 连接断开后或实例销毁后停止心跳
   */
  stopHeartbeat(): void;
  /**
   * 销毁实例
   */
  destroy(): void;
  _logError(error: any): void;
}

类型

import { IncomingResponseMessage } from "sip.js/lib/core";
export declare class SosInfo {
  cmd?: string;
  type?: string;
  room_id?: string;
  ca_info?: CAInfo;
  x_point?: string;
  y_point?: string;
  time?: number;
}
export declare class CAInfo {
  user_id?: string;
  device_id?: string;
  ca_ver?: string;
  mobile?: string;
  sim?: string;
  pwd?: string;
  c_time?: string;
  app_last_login_time?: string;
  ca_last_login_time?: string;
  user_name?: string;
  real_name?: string;
  user_img?: string;
  department?: string;
  role?: string;
  f_id?: string;
  cap_type?: string;
  b1?: string;
  b2?: string;
  sos_height?: string;
  tk?: string;
  nearvolt?: string;
  health?: string;
  pressure?: string;
  account_id?: string;
  w_id?: string;
  acc_list?: string;
  c_user_id?: string;
  gas_volt?: string;
  new_low?: string;
  uwb_device_id?: string;
  rtmp_open?: string;
  voice_pkg?: string;
  fb_id?: string;
  min_o2?: string;
  max_o2?: string;
  watch?: string;
  local_record?: string;
  last_count_online_time?: string;
  watch_mac?: string;
  co?: string;
  app_version?: string;
  min_temp?: string;
  max_temp?: string;
  max_heartbeat?: string;
  min_heartbeat?: string;
  shrink?: string;
  diastolic?: string;
  video_pre?: string;
  push_sos_url?: string;
  electrostatic?: string;
  headset?: string;
  upload_video_num?: string;
  notuploaded_video_count?: string;
  privacy_switch?: string;
  boot_broadcast?: string;
  gas_mac?: string;
  check_upgrade?: string;
  new_app_id?: string;
  check_system_upgrade?: string;
  new_system_app_id?: string;
}
export declare class DeviceInfo {
  location_info?: LocationInfo;
  user_info?: UserInfo;
}
export declare class UserInfo {
  account_id?: string;
  app_last_login_time?: string;
  app_version?: string;
  b1?: string;
  b2?: string;
  boot_broadcast?: string;
  c_time?: string;
  c_user_id?: string;
  ca_last_login_time?: string;
  ca_ver?: string;
  cap_type?: string;
  co?: string;
  department?: string;
  device_id?: string;
  diastolic?: string;
  electrostatic?: string;
  f_id?: string;
  fb_id?: string;
  g_id?: string;
  gas_volt?: string;
  headset?: string;
  health?: string;
  last_count_online_time?: string;
  local_record?: string;
  max_heartbeat?: string;
  max_o2?: string;
  max_temp?: string;
  min_heartbeat?: string;
  min_o2?: string;
  min_temp?: string;
  mobile?: string;
  nearvolt?: string;
  new_low?: string;
  notuploaded_video_count?: string;
  pressure?: string;
  privacy_switch?: string;
  push_sos_url?: string;
  real_name?: string;
  role?: string;
  rtmp_open?: string;
  shrink?: string;
  sim?: string;
  sip_id?: string;
  sos_height?: string;
  tk?: string;
  upload_video_num?: string;
  user_id?: string;
  user_img?: string;
  user_name?: string;
  uwb_device_id?: string;
  video_pre?: string;
  voice_pkg?: string;
  w_id?: string;
  watch?: string;
  watch_mac?: string;
}
export declare class LocationInfo {
  Altitude?: string;
  act?: string;
  bat_l?: string;
  bat_v?: string;
  body_info?: string;
  body_simple_info?: string;
  bt_signal_info?: string;
  c_angle?: string;
  c_speed?: string;
  c_time?: string;
  c_trust?: string;
  charging?: string;
  ctime?: string;
  gas_data?: string;
  gps?: string;
  gps_level?: string;
  in_use?: string;
  is_weared?: string;
  net_strenth?: string;
  net_type?: string;
  notuploaded_video_count?: string;
  oxygen_data?: string;
  phoneNumber?: string;
  rail_status?: string;
  record_switch?: string;
  sim_data_num?: string;
  sim_status?: string;
  tcard_status?: string;
  tempdata?: string;
  user_id?: string;
  x_point?: string;
  y_point?: string;
}
/**
 * 润德设备配置信息
 */
export interface RundeOpt {
  /**
   * API 接口地址
   */
  host?: string;
  /**
   * 账号
   */
  name: string;
  /**
   * 密码
   */
  password: string;
  /**
   * websocket 连接地址
   */
  wsurl: string;
  /**
   * 通过接口登录,舍弃
   */
  newApi?: boolean;
  /**
   * 心跳间隔,会影响实时获取在线设备的刷新,可以通过手动获取实时设备达到预期效果。
   */
  poolDuration?: number;
}
/**
 * 指令消息格式
 */
export declare type CMDMessage = {
  cmd: string;
  status: boolean;
  msg: string;
  data: any;
} & any;
/**
 * 润德SIP信息
 */
export interface SIPInfo {
  sip_host: string;
  sip_id: number;
  sip_pwd: string;
  stun_host: string;
  turn_host: string;
  turn_pwd: string;
  turn_user: string;
  wss_url: string;
}
/**
 * 管理员账号信息
 */
export interface AdminInfo {
  acc_list: string;
  account_id: string;
  admin_id: string;
  c_time: string;
  department: string;
  device_list: string;
  expire_time: string;
  group_room_list: string;
  is_top: number;
  last_login_time: string;
  mobile: string;
  p_id: string;
  permission_list: string;
  pwd: string;
  pwd2: string;
  role: string;
  rtmp_open: string;
  user_name: string;
  sip_info: SIPInfo;
}
/**
 * 监听器
 */
export interface MsgListener {
  key?: string;
  cmd?: string | CMD;
  callback?: Function;
  cmdMessage?: (data: CMDMessage) => void;
  callState?: (type: CALL_STATE, data?: any) => void;
  requestDelegate?: (
    type: CALL_STATE,
    response: IncomingResponseMessage
  ) => void;
}
export declare enum CMD {
  LOGIN = "ma_login",
  GET_ONLINE_DEVICES = "ma_get_active_devices",
  CALL_SIP = "ma_set_sip_info",
  /**
   * 安全帽紧急呼叫SOS
   */
  SOS = "server_push_ca_sip_sos",
}
export declare enum CALL_STATE {
  /**
   * 呼叫中
   */
  ON_PROGRESS = "onProgress",
  /**
   * 接通时
   */
  ON_ACCEPT = "onAccept",
  /**
   * 拒绝时
   */
  ON_REJECT = "onReject",
  /**
   * Called when a call is answered.
   * @remarks
   * Callback for handling establishment of a new Session.
   */
  onCallAnswered = "onCallAnswered",
  /**
   * Called when a call is created.
   * @remarks
   * Callback for handling the creation of a new Session.
   */
  onCallCreated = "onCallCreated",
  /**
   * Called when a call is received.
   * @remarks
   * Callback for handling incoming INVITE requests.
   * The callback must either accept or reject the incoming call by calling `answer()` or `decline()` respectively.
   */
  onCallReceived = "onCallReceived",
  /**
   * Called when a call is hung up.
   * @remarks
   * Callback for handling termination of a Session.
   */
  onCallHangup = "onCallHangup",
  /**
   * Called when a call is put on hold or taken off hold.
   * @remarks
   * Callback for handling re-INVITE responses.
   */
  onCallHold = "onCallHold",
  /**
   * Called when a call receives an incoming DTMF tone.
   * @remarks
   * Callback for handling an incoming INFO request with content type application/dtmf-relay.
   */
  onCallDTMFReceived = "onCallDTMFReceived",
  /**
   * Called upon receiving a message.
   * @remarks
   * Callback for handling incoming MESSAGE requests.
   * @param message - The message received.
   */
  onMessageReceived = "onMessageReceived",
  /**
   * Called when user is registered to received calls.
   */
  onRegistered = "onRegistered",
  /**
   * Called when user is no longer registered to received calls.
   */
  onUnregistered = "onUnregistered",
  /**
   * Called when user is connected to server.
   * @remarks
   * Callback for handling user becomes connected.
   */
  onServerConnect = "onServerConnect",
  /**
   * Called when user is no longer connected.
   * @remarks
   * Callback for handling user becomes disconnected.
   *
   * @param error - An Error if server caused the disconnect. Otherwise undefined.
   */
  onServerDisconnect = "onServerDisconnect",
}

SIP 工具类

import { SimpleUser, SimpleUserOptions } from "sip.js/lib/platform/web";
import { AdminInfo } from "./class";
export declare function getAudioElement(id: string): HTMLAudioElement;
export declare function getVideoElement(id: string): HTMLVideoElement;
export declare const useSimpleUA: (
  admin: AdminInfo,
  sipOption: SimpleUserOptions
) => Promise<SimpleUser>;